Completed
Push — master ( 503784...ece649 )
by Lars
01:38
created

Arrayy   F

Complexity

Total Complexity 675

Size/Duplication

Total Lines 6922
Duplicated Lines 8.8 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 92.56%

Importance

Changes 0
Metric Value
dl 609
loc 6922
ccs 1792
cts 1936
cp 0.9256
rs 0.8
c 0
b 0
f 0
wmc 675
lcom 1
cbo 5

249 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 17 1
A __clone() 0 10 3
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 append() 0 24 5
A asort() 0 8 1
A asortImmutable() 0 11 1
A count() 0 12 3
A exchangeArray() 0 6 1
A getArrayCopy() 0 6 1
A getIterator() 0 17 3
A getIteratorClass() 0 4 1
A ksort() 0 8 1
A ksortImmutable() 0 11 1
A natcasesort() 0 8 1
A natcasesortImmutable() 0 11 1
A natsort() 0 8 1
A natsortImmutable() 0 11 1
B offsetExists() 25 62 9
A offsetGet() 0 11 2
A offsetSet() 0 18 3
B offsetUnset() 28 51 8
A serialize() 0 10 2
A setIteratorClass() 0 22 4
A uasort() 12 12 2
A uasortImmutable() 0 11 1
A uksort() 0 4 1
A uksortImmutable() 0 4 1
A unserialize() 0 10 2
B appendArrayValues() 0 26 7
A appendToEachKey() 19 19 4
A appendToEachValue() 19 19 5
A arsort() 0 8 1
A arsortImmutable() 0 10 1
A at() 0 14 2
A average() 0 14 3
B changeKeyCase() 0 29 7
A changeSeparator() 0 6 1
A chunk() 0 8 1
A clean() 0 8 1
A clear() 0 19 4
B containsCaseInsensitive() 0 31 9
A containsKey() 0 4 1
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 11 1
B flatten() 0 23 6
A createByReference() 0 8 1
A createFromGeneratorFunction() 0 4 1
A createFromGeneratorImmutable() 0 4 1
A createFromJson() 0 4 1
A createFromArray() 0 4 1
A createFromObject() 0 20 3
A createFromObjectVars() 0 4 1
A createFromString() 0 32 5
A createFromTraversableImmutable() 0 4 1
A createWithRange() 0 4 1
A current() 0 4 1
A customSortKeys() 0 8 1
A customSortKeysImmutable() 0 13 1
A customSortValues() 12 12 2
A customSortValuesImmutable() 0 11 1
A delete() 0 8 2
A diff() 0 8 1
A diffKey() 0 8 1
A diffKeyAndValue() 0 8 1
B diffRecursive() 0 35 7
A diffReverse() 0 8 1
A divide() 0 11 1
A each() 11 15 2
A end() 0 4 1
A exists() 0 15 3
A fillWithDefaults() 0 23 3
A filter() 0 12 2
B filterBy() 0 72 3
A find() 10 10 3
A findBy() 0 4 1
A first() 0 9 2
A firstKey() 0 6 1
A firstsImmutable() 16 16 2
A firstsKeys() 16 16 2
A firstsMutable() 0 12 2
A flip() 0 8 1
A add() 0 18 5
B contains() 0 22 6
F get() 6 165 37
A getAll() 0 4 1
A getArray() 0 9 1
A createFromJsonMapper() 5 16 2
A getPhpDocPropertiesFromClass() 0 8 2
A getList() 0 4 1
A getColumn() 0 8 1
A getGeneratorByReference() 0 19 4
A getGenerator() 0 10 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
A getValues() 0 10 1
A getValuesYield() 0 4 1
B group() 0 41 7
B has() 0 26 6
A hasValue() 0 4 1
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 26 3
A intersectionMulti() 0 8 1
A intersects() 0 4 1
A invoke() 24 24 3
A isAssoc() 14 15 4
A isEmpty() 0 18 5
A isEqual() 0 4 1
A isMultiArray() 0 8 1
A isNumeric() 14 15 4
A isSequential() 0 17 2
A jsonSerialize() 0 4 1
A key() 0 4 1
A keyExists() 0 4 1
C keys() 25 71 13
A krsort() 0 8 1
A krsortImmutable() 0 11 1
A last() 0 9 2
A lastKey() 0 6 1
A lastsImmutable() 0 30 4
A lastsMutable() 0 28 4
A length() 0 4 1
A map() 0 33 5
A matches() 16 16 4
A matchesAny() 16 16 4
A max() 19 20 5
A mergeAppendKeepIndex() 15 15 2
A mergeAppendNewIndex() 15 15 2
A mergePrependKeepIndex() 15 15 2
A mergePrependNewIndex() 15 15 2
A meta() 0 4 1
A min() 19 20 5
A mostUsedValue() 0 4 1
A mostUsedValues() 0 4 1
B moveElement() 0 33 6
A moveElementToFirstPlace() 16 16 2
A moveElementToLastPlace() 16 16 2
A next() 0 4 1
A nth() 0 19 3
A only() 0 10 1
A pad() 0 8 1
A partition() 0 16 3
A pop() 0 6 1
A prepend() 0 16 3
A prependToEachKey() 26 26 4
A prependToEachValue() 28 28 5
A pull() 0 22 4
A push() 0 18 4
A randomImmutable() 6 33 3
A randomKey() 0 10 2
A randomKeys() 0 28 3
A randomMutable() 7 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 23 5
A reindex() 0 8 1
A reject() 12 17 3
A remove() 0 23 3
A removeElement() 0 4 1
A removeFirst() 12 12 1
A removeLast() 12 12 1
A removeValue() 0 23 4
A repeat() 0 12 2
A replace() 0 10 1
A replaceAllKeys() 0 8 1
A replaceAllValues() 0 8 1
A replaceKeys() 0 11 1
A replaceOneValue() 15 15 2
A replaceValues() 0 12 1
A rest() 10 10 1
A reverse() 0 8 1
A rsort() 0 8 1
A rsortImmutable() 0 11 1
A searchIndex() 0 10 3
A searchValue() 0 30 4
A set() 0 6 1
A setAndGet() 0 11 2
A shift() 0 6 1
B shuffle() 0 38 8
A size() 0 4 1
A sizeIs() 0 16 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 14 1
A sortImmutable() 0 16 1
A sortKeys() 0 10 1
A sortKeysImmutable() 0 13 1
A sortValueKeepIndex() 0 6 1
A sortValueNewIndex() 0 4 1
A sorter() 0 41 3
A splice() 0 17 1
A split() 0 19 2
A stripEmpty() 0 12 2
A swap() 0 12 1
A toArray() 0 25 5
A toList() 0 7 1
A toJson() 0 9 2
A toPermutation() 0 34 4
A toString() 0 4 1
A unique() 0 22 2
A uniqueKeepIndex() 0 26 2
A uniqueNewIndex() 0 4 1
A unshift() 0 8 1
A validate() 10 10 3
A values() 0 13 2
A walk() 0 22 5
A where() 0 13 1
A arrayToObject() 0 19 4
C array_keys_recursive() 25 61 16
A callAtPath() 0 29 5
A extractValue() 0 22 5
A fallbackForArray() 0 10 2
A generatorToArray() 0 11 2
A getDirection() 0 22 5
B getPropertiesFromPhpDoc() 0 31 6
B implode_recursive() 0 33 9
B in_array_recursive() 0 25 6
C internalGetArray() 0 51 12
B internalRemove() 0 33 7
C internalSet() 0 59 11
A objectToArray() 0 13 2
B setInitialValuesAndProperties() 0 45 10
A sorterKeys() 0 21 5
B sorting() 0 30 8
A getArrayRecursiveHelperArrayy() 0 22 3
B checkType() 0 18 7

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
/** @noinspection ReturnTypeCanBeDeclaredInspection */
4
/** @noinspection ClassReImplementsParentInterfaceInspection */
5
6
declare(strict_types=1);
7
8
namespace Arrayy;
9
10
use Arrayy\Type\TypeInterface;
11
use Arrayy\TypeCheck\TypeCheckArray;
12
use Arrayy\TypeCheck\TypeCheckInterface;
13
use Arrayy\TypeCheck\TypeCheckPhpDoc;
14
15
/**
16
 * Methods to manage arrays.
17
 *
18
 * For the full copyright and license information, please view the LICENSE
19
 * file that was distributed with this source code.
20
 *
21
 * @template TKey of array-key
22
 * @template T
23
 * @template-extends \ArrayObject<TKey,T>
24
 * @template-implements \IteratorAggregate<TKey,T>
25
 * @template-implements \ArrayAccess<TKey|null,T>
26
 */
27
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
28
{
29
    const ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES = '!!!!Arrayy_Helper_Types_For_All_Properties!!!!';
30
31
    const ARRAYY_HELPER_WALK = '!!!!Arrayy_Helper_Walk!!!!';
32
33
    /**
34
     * @var array
35
     *
36
     * @psalm-var array<mixed,mixed>|array<TKey,T>
37
     */
38
    protected $array = [];
39
40
    /**
41
     * @var \Arrayy\ArrayyRewindableGenerator|null
42
     *
43
     * @psalm-var \Arrayy\ArrayyRewindableGenerator<TKey,T>|null
44
     */
45
    protected $generator;
46
47
    /**
48
     * @var string
49
     *
50
     * @psalm-var class-string<\Arrayy\ArrayyIterator>
51
     */
52
    protected $iteratorClass = ArrayyIterator::class;
53
54
    /**
55
     * @var string
56
     */
57
    protected $pathSeparator = '.';
58
59
    /**
60
     * @var bool
61
     */
62
    protected $checkPropertyTypes = false;
63
64
    /**
65
     * @var bool
66
     */
67
    protected $checkForMissingPropertiesInConstructor = false;
68
69
    /**
70
     * @var bool
71
     */
72
    protected $checkPropertiesMismatchInConstructor = false;
73
74
    /**
75
     * @var bool
76
     */
77
    protected $checkPropertiesMismatch = true;
78
79
    /**
80
     * @var array<int|string,TypeCheckInterface>|mixed|TypeCheckArray<int|string,TypeCheckInterface>|TypeInterface
81
     */
82
    protected $properties = [];
83
84
    /**
85
     * Initializes
86
     *
87
     * @param mixed  $data                         <p>
88
     *                                             Should be an array or a generator, otherwise it will try
89
     *                                             to convert it into an array.
90
     *                                             </p>
91
     * @param string $iteratorClass                optional <p>
92
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
93
     *                                             need this option.
94
     *                                             </p>
95
     * @param bool   $checkPropertiesInConstructor optional <p>
96
     *                                             You need to extend the "Arrayy"-class and you need to set
97
     *                                             the $checkPropertiesMismatchInConstructor class property
98
     *                                             to
99
     *                                             true, otherwise this option didn't not work anyway.
100
     *                                             </p>
101
     *
102
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
103
     */
104 1198
    public function __construct(
105
        $data = [],
106
        string $iteratorClass = ArrayyIterator::class,
107
        bool $checkPropertiesInConstructor = true
108
    ) {
109 1198
        $data = $this->fallbackForArray($data);
110
111
        // used only for serialize + unserialize, all other methods are overwritten
112
        /**
113
         * @psalm-suppress InvalidArgument - why?
114
         */
115 1196
        parent::__construct([], 0, $iteratorClass);
116
117 1196
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
118
119 1189
        $this->setIteratorClass($iteratorClass);
120 1189
    }
121
122
    /**
123
     * @return void
124
     */
125 51
    public function __clone()
126
    {
127 51
        if (!\is_array($this->properties)) {
128
            $this->properties = clone $this->properties;
0 ignored issues
show
Documentation Bug introduced by
It seems like clone $this->properties of type object is incompatible with the declared type array of property $properties.

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...
129
        }
130
131 51
        if ($this->generator !== null) {
132
            $this->generator = clone $this->generator;
133
        }
134 51
    }
135
136
    /**
137
     * Call object as function.
138
     *
139
     * @param mixed $key
140
     *
141
     * @return mixed
142
     */
143 1
    public function __invoke($key = null)
144
    {
145 1
        if ($key !== null) {
146 1
            $this->generatorToArray();
147
148 1
            return $this->array[$key] ?? false;
149
        }
150
151
        return $this->toArray();
152
    }
153
154
    /**
155
     * Whether or not an element exists by key.
156
     *
157
     * @param mixed $key
158
     *
159
     * @return bool
160
     *              <p>True is the key/index exists, otherwise false.</p>
161
     */
162
    public function __isset($key): bool
163
    {
164
        return $this->offsetExists($key);
165
    }
166
167
    /**
168
     * Assigns a value to the specified element.
169
     *
170
     * @param mixed $key
171
     * @param mixed $value
172
     *
173
     * @return void
174
     */
175 3
    public function __set($key, $value)
176
    {
177 3
        $this->internalSet($key, $value);
178 3
    }
179
180
    /**
181
     * magic to string
182
     *
183
     * @return string
184
     */
185 15
    public function __toString(): string
186
    {
187 15
        return $this->toString();
188
    }
189
190
    /**
191
     * Unset element by key.
192
     *
193
     * @param mixed $key
194
     */
195
    public function __unset($key)
196
    {
197
        $this->internalRemove($key);
198
    }
199
200
    /**
201
     * Get a value by key.
202
     *
203
     * @param mixed $key
204
     *
205
     * @return mixed
206
     *               <p>Get a Value from the current array.</p>
207
     */
208 126
    public function &__get($key)
209
    {
210 126
        $return = $this->get($key, null, null, true);
211
212 126
        if (\is_array($return) === true) {
213
            $return = static::create($return, $this->iteratorClass, false);
214
        }
215
216 126
        return $return;
217
    }
218
219
    /**
220
     * Add new values (optional using dot-notation).
221
     *
222
     * @param mixed           $value
223
     * @param int|string|null $key
224
     *
225
     * @return static
226
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
227
     *
228
     * @psalm-param  T $value
229
     * @psalm-return static<TKey,T>
230
     */
231 13
    public function add($value, $key = null)
232
    {
233 13
        if ($key !== null) {
234 5
            $get = $this->get($key);
235 5
            if ($get !== null) {
236 1
                $value = \array_merge_recursive(
237 1
                    !$get instanceof self ? [$get] : $get->getArray(),
238 1
                    !\is_array($value) ? [$value] : $value
239
                );
240
            }
241
242 5
            $this->internalSet($key, $value);
243
244 4
            return $this;
245
        }
246
247 8
        return $this->append($value);
248
    }
249
250
    /**
251
     * Append a (key) + value to the current array.
252
     *
253
     * EXAMPLE: <code>
254
     * a(['fòô' => 'bàř'])->append('foo'); // Arrayy['fòô' => 'bàř', 0 => 'foo']
255
     * </code>
256
     *
257
     * @param mixed $value
258
     * @param mixed $key
259
     *
260
     * @return $this
261
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
262
     *
263
     * @psalm-return static<TKey,T>
264
     */
265 20
    public function append($value, $key = null): self
266
    {
267 20
        $this->generatorToArray();
268
269 20
        if ($this->properties !== []) {
270 6
            $this->checkType($key, $value);
271
        }
272
273 19
        if ($key !== null) {
274
            if (
275 2
                isset($this->array[$key])
276
                &&
277 2
                \is_array($this->array[$key]) === true
278
            ) {
279
                $this->array[$key][] = $value;
280
            } else {
281 2
                $this->array[$key] = $value;
282
            }
283
        } else {
284 17
            $this->array[] = $value;
285
        }
286
287 19
        return $this;
288
    }
289
290
    /**
291
     * Sort the entries by value.
292
     *
293
     * @param int $sort_flags [optional] <p>
294
     *                        You may modify the behavior of the sort using the optional
295
     *                        parameter sort_flags, for details
296
     *                        see sort.
297
     *                        </p>
298
     *
299
     * @return $this
300
     *               <p>(Mutable) Return this Arrayy object.</p>
301
     *
302
     * @psalm-return static<TKey,T>
303
     */
304 4
    public function asort(int $sort_flags = 0): self
305
    {
306 4
        $this->generatorToArray();
307
308 4
        \asort($this->array, $sort_flags);
309
310 4
        return $this;
311
    }
312
313
    /**
314
     * Sort the entries by value.
315
     *
316
     * @param int $sort_flags [optional] <p>
317
     *                        You may modify the behavior of the sort using the optional
318
     *                        parameter sort_flags, for details
319
     *                        see sort.
320
     *                        </p>
321
     *
322
     * @return $this
323
     *               <p>(Immutable) Return this Arrayy object.</p>
324
     *
325
     * @psalm-return static<TKey,T>
326
     * @psalm-mutation-free
327
     */
328 4
    public function asortImmutable(int $sort_flags = 0): self
329
    {
330 4
        $that = clone $this;
331
332
        /**
333
         * @psalm-suppress ImpureMethodCall - object is already cloned
334
         */
335 4
        $that->asort($sort_flags);
336
337 4
        return $that;
338
    }
339
340
    /**
341
     * Counts all elements in an array, or something in an object.
342
     *
343
     * <p>
344
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
345
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
346
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
347
     * implemented and used in PHP.
348
     * </p>
349
     *
350
     * @see http://php.net/manual/en/function.count.php
351
     *
352
     * @param int $mode [optional] If the optional mode parameter is set to
353
     *                  COUNT_RECURSIVE (or 1), count
354
     *                  will recursively count the array. This is particularly useful for
355
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
356
     *
357
     * @return int
358
     *             <p>
359
     *             The number of elements in var, which is
360
     *             typically an array, since anything else will have one
361
     *             element.
362
     *             </p>
363
     *             <p>
364
     *             If var is not an array or an object with
365
     *             implemented Countable interface,
366
     *             1 will be returned.
367
     *             There is one exception, if var is &null;,
368
     *             0 will be returned.
369
     *             </p>
370
     *             <p>
371
     *             Caution: count may return 0 for a variable that isn't set,
372
     *             but it may also return 0 for a variable that has been initialized with an
373
     *             empty array. Use isset to test if a variable is set.
374
     *             </p>
375
     * @psalm-mutation-free
376
     */
377 148
    public function count(int $mode = \COUNT_NORMAL): int
378
    {
379
        if (
380 148
            $this->generator
381
            &&
382 148
            $mode === \COUNT_NORMAL
383
        ) {
384 4
            return \iterator_count($this->generator);
385
        }
386
387 144
        return \count($this->toArray(), $mode);
388
    }
389
390
    /**
391
     * Exchange the array for another one.
392
     *
393
     * @param array|static $data
394
     *
395
     * @return array
396
     *
397
     * @psalm-param  array<TKey,T>|self<TKey,T> $data
398
     * @psalm-return array<mixed,mixed>|array<TKey,T>
399
     */
400 1
    public function exchangeArray($data): array
401
    {
402 1
        $this->array = $this->fallbackForArray($data);
403
404 1
        return $this->array;
405
    }
406
407
    /**
408
     * Creates a copy of the ArrayyObject.
409
     *
410
     * @return array
411
     *
412
     * @psalm-return array<mixed,mixed>|array<TKey,T>
413
     */
414 6
    public function getArrayCopy(): array
415
    {
416 6
        $this->generatorToArray();
417
418 6
        return $this->array;
419
    }
420
421
    /**
422
     * Returns a new iterator, thus implementing the \Iterator interface.
423
     *
424
     * @return \Iterator<mixed, mixed>
0 ignored issues
show
Documentation introduced by
The doc-type \Iterator<mixed, could not be parsed: Expected "|" or "end of type", but got "<" at position 9. (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...
425
     *                          <p>An iterator for the values in the array.</p>
426
     * @psalm-return \Iterator<array-key|TKey, mixed|T>
427
     */
428 27
    public function getIterator(): \Iterator
429
    {
430 27
        if ($this->generator instanceof ArrayyRewindableGenerator) {
431 1
            return $this->generator;
432
        }
433
434 26
        $iterator = $this->getIteratorClass();
435
436 26
        if ($iterator === ArrayyIterator::class) {
437 26
            return new $iterator($this->toArray(), 0, static::class);
438
        }
439
440
        $return = new $iterator($this->toArray());
441
        \assert($return instanceof \Iterator);
442
443
        return $return;
444
    }
445
446
    /**
447
     * Gets the iterator classname for the ArrayObject.
448
     *
449
     * @return string
450
     *
451
     * @psalm-return class-string
452
     */
453 26
    public function getIteratorClass(): string
454
    {
455 26
        return $this->iteratorClass;
456
    }
457
458
    /**
459
     * Sort the entries by key.
460
     *
461
     * @param int $sort_flags [optional] <p>
462
     *                        You may modify the behavior of the sort using the optional
463
     *                        parameter sort_flags, for details
464
     *                        see sort.
465
     *                        </p>
466
     *
467
     * @return $this
468
     *               <p>(Mutable) Return this Arrayy object.</p>
469
     *
470
     * @psalm-return static<TKey,T>
471
     */
472 4
    public function ksort(int $sort_flags = 0): self
473
    {
474 4
        $this->generatorToArray();
475
476 4
        \ksort($this->array, $sort_flags);
477
478 4
        return $this;
479
    }
480
481
    /**
482
     * Sort the entries by key.
483
     *
484
     * @param int $sort_flags [optional] <p>
485
     *                        You may modify the behavior of the sort using the optional
486
     *                        parameter sort_flags, for details
487
     *                        see sort.
488
     *                        </p>
489
     *
490
     * @return $this
491
     *               <p>(Immutable) Return this Arrayy object.</p>
492
     *
493
     * @psalm-return static<TKey,T>
494
     */
495 4
    public function ksortImmutable(int $sort_flags = 0): self
496
    {
497 4
        $that = clone $this;
498
499
        /**
500
         * @psalm-suppress ImpureMethodCall - object is already cloned
501
         */
502 4
        $that->ksort($sort_flags);
503
504 4
        return $that;
505
    }
506
507
    /**
508
     * Sort an array using a case insensitive "natural order" algorithm.
509
     *
510
     * @return $this
511
     *               <p>(Mutable) Return this Arrayy object.</p>
512
     *
513
     * @psalm-return static<TKey,T>
514
     */
515 8
    public function natcasesort(): self
516
    {
517 8
        $this->generatorToArray();
518
519 8
        \natcasesort($this->array);
520
521 8
        return $this;
522
    }
523
524
    /**
525
     * Sort an array using a case insensitive "natural order" algorithm.
526
     *
527
     * @return $this
528
     *               <p>(Immutable) Return this Arrayy object.</p>
529
     *
530
     * @psalm-return static<TKey,T>
531
     * @psalm-mutation-free
532
     */
533 4
    public function natcasesortImmutable(): self
534
    {
535 4
        $that = clone $this;
536
537
        /**
538
         * @psalm-suppress ImpureMethodCall - object is already cloned
539
         */
540 4
        $that->natcasesort();
541
542 4
        return $that;
543
    }
544
545
    /**
546
     * Sort entries using a "natural order" algorithm.
547
     *
548
     * @return $this
549
     *               <p>(Mutable) Return this Arrayy object.</p>
550
     *
551
     * @psalm-return static<TKey,T>
552
     */
553 10
    public function natsort(): self
554
    {
555 10
        $this->generatorToArray();
556
557 10
        \natsort($this->array);
558
559 10
        return $this;
560
    }
561
562
    /**
563
     * Sort entries using a "natural order" algorithm.
564
     *
565
     * @return $this
566
     *               <p>(Immutable) Return this Arrayy object.</p>
567
     *
568
     * @psalm-return static<TKey,T>
569
     * @psalm-mutation-free
570
     */
571 4
    public function natsortImmutable(): self
572
    {
573 4
        $that = clone $this;
574
575
        /**
576
         * @psalm-suppress ImpureMethodCall - object is already cloned
577
         */
578 4
        $that->natsort();
579
580 4
        return $that;
581
    }
582
583
    /**
584
     * Whether or not an offset exists.
585
     *
586
     * @param bool|int|string $offset
587
     *
588
     * @return bool
589
     *
590
     * @noinspection PhpSillyAssignmentInspection
591
     *
592
     * @psalm-mutation-free
593
     */
594 157
    public function offsetExists($offset): bool
595
    {
596 157
        $this->generatorToArray();
597
598 157
        if ($this->array === []) {
599 8
            return false;
600
        }
601
602
        // php cast "bool"-index into "int"-index
603 151
        if ((bool) $offset === $offset) {
604 1
            $offset = (int) $offset;
605
        }
606
607
        /** @var int|string $offset - hint for phpstan */
608 151
        $offset = $offset;
0 ignored issues
show
Bug introduced by
Why assign $offset to itself?

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
609
610 151
        $tmpReturn = $this->keyExists($offset);
611
612
        if (
613 151
            $tmpReturn === true
614
            ||
615 151
            \strpos((string) $offset, $this->pathSeparator) === false
616
        ) {
617 148
            return $tmpReturn;
618
        }
619
620 4
        $offsetExists = false;
621
622
        /**
623
         * https://github.com/vimeo/psalm/issues/2536
624
         *
625
         * @psalm-suppress PossiblyInvalidArgument
626
         * @psalm-suppress InvalidScalarArgument
627
         */
628 View Code Duplication
        if (
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...
629 4
            $this->pathSeparator
630
            &&
631 4
            (string) $offset === $offset
632
            &&
633 4
            \strpos($offset, $this->pathSeparator) !== false
634
        ) {
635 4
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
636 4
            if ($explodedPath !== false) {
637
                /** @var string $lastOffset - helper for phpstan */
638 4
                $lastOffset = \array_pop($explodedPath);
639 4
                $containerPath = \implode($this->pathSeparator, $explodedPath);
640
641
                /**
642
                 * @psalm-suppress MissingClosureReturnType
643
                 * @psalm-suppress MissingClosureParamType
644
                 */
645 4
                $this->callAtPath(
646 4
                    $containerPath,
647
                    static function ($container) use ($lastOffset, &$offsetExists) {
648 4
                        $offsetExists = \array_key_exists($lastOffset, $container);
649 4
                    }
650
                );
651
            }
652
        }
653
654 4
        return $offsetExists;
655
    }
656
657
    /**
658
     * Returns the value at specified offset.
659
     *
660
     * @param int|string $offset
661
     *
662
     * @return mixed
663
     *               <p>Will return null if the offset did not exists.</p>
664
     */
665 126
    public function &offsetGet($offset)
666
    {
667
        // init
668 126
        $value = null;
669
670 126
        if ($this->offsetExists($offset)) {
671 124
            $value = &$this->__get($offset);
672
        }
673
674 126
        return $value;
675
    }
676
677
    /**
678
     * Assigns a value to the specified offset + check the type.
679
     *
680
     * @param int|string|null $offset
681
     * @param mixed           $value
682
     *
683
     * @return void
684
     */
685 27
    public function offsetSet($offset, $value)
686
    {
687 27
        $this->generatorToArray();
688
689 27
        if ($offset === null) {
690 6
            if ($this->properties !== []) {
691 1
                $this->checkType(null, $value);
692
            }
693
694 5
            $this->array[] = $value;
695
        } else {
696 21
            $this->internalSet(
697 21
                $offset,
698 21
                $value,
699 21
                true
700
            );
701
        }
702 26
    }
703
704
    /**
705
     * Unset an offset.
706
     *
707
     * @param int|string $offset
708
     *
709
     * @return void
710
     *              <p>(Mutable) Return nothing.</p>
711
     */
712 25
    public function offsetUnset($offset)
713
    {
714 25
        $this->generatorToArray();
715
716 25
        if ($this->array === []) {
717 6
            return;
718
        }
719
720 20
        if ($this->keyExists($offset)) {
721 13
            unset($this->array[$offset]);
722
723 13
            return;
724
        }
725
726
        /**
727
         * https://github.com/vimeo/psalm/issues/2536
728
         *
729
         * @psalm-suppress PossiblyInvalidArgument
730
         * @psalm-suppress InvalidScalarArgument
731
         */
732 View Code Duplication
        if (
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...
733 10
            $this->pathSeparator
734
            &&
735 10
            (string) $offset === $offset
736
            &&
737 10
            \strpos($offset, $this->pathSeparator) !== false
738
        ) {
739 7
            $path = \explode($this->pathSeparator, (string) $offset);
740
741 7
            if ($path !== false) {
742 7
                $pathToUnset = \array_pop($path);
743
744
                /**
745
                 * @psalm-suppress MissingClosureReturnType
746
                 * @psalm-suppress MissingClosureParamType
747
                 */
748 7
                $this->callAtPath(
749 7
                    \implode($this->pathSeparator, $path),
750
                    static function (&$offset) use ($pathToUnset) {
751 6
                        if (\is_array($offset)) {
752 5
                            unset($offset[$pathToUnset]);
753
                        } else {
754 1
                            $offset = null;
755
                        }
756 7
                    }
757
                );
758
            }
759
        }
760
761 10
        unset($this->array[$offset]);
762 10
    }
763
764
    /**
765
     * Serialize the current "Arrayy"-object.
766
     *
767
     * @return string
768
     */
769 2
    public function serialize(): string
770
    {
771 2
        $this->generatorToArray();
772
773 2
        if (\PHP_VERSION_ID < 70400) {
774 2
            return parent::serialize();
775
        }
776
777
        return \serialize($this);
778
    }
779
780
    /**
781
     * Sets the iterator classname for the current "Arrayy"-object.
782
     *
783
     * @param string $iteratorClass
784
     *
785
     * @throws \InvalidArgumentException
786
     *
787
     * @return void
788
     *
789
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
790
     */
791 1189
    public function setIteratorClass($iteratorClass)
792
    {
793 1189
        if (\class_exists($iteratorClass)) {
794 1189
            $this->iteratorClass = $iteratorClass;
795
796 1189
            return;
797
        }
798
799
        if (\strpos($iteratorClass, '\\') === 0) {
800
            $iteratorClass = '\\' . $iteratorClass;
801
            if (\class_exists($iteratorClass)) {
802
                /**
803
                 * @psalm-suppress PropertyTypeCoercion
804
                 */
805
                $this->iteratorClass = $iteratorClass;
806
807
                return;
808
            }
809
        }
810
811
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
812
    }
813
814
    /**
815
     * Sort the entries with a user-defined comparison function and maintain key association.
816
     *
817
     * @param callable $function
818
     *
819
     * @throws \InvalidArgumentException
820
     *
821
     * @return $this
822
     *               <p>(Mutable) Return this Arrayy object.</p>
823
     *
824
     * @psalm-return static<TKey,T>
825
     */
826 8 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...
827
    {
828 8
        if (!\is_callable($function)) {
829
            throw new \InvalidArgumentException('Passed function must be callable');
830
        }
831
832 8
        $this->generatorToArray();
833
834 8
        \uasort($this->array, $function);
835
836 8
        return $this;
837
    }
838
839
    /**
840
     * Sort the entries with a user-defined comparison function and maintain key association.
841
     *
842
     * @param callable $function
843
     *
844
     * @throws \InvalidArgumentException
845
     *
846
     * @return $this
847
     *               <p>(Immutable) Return this Arrayy object.</p>
848
     *
849
     * @psalm-return static<TKey,T>
850
     * @psalm-mutation-free
851
     */
852 4
    public function uasortImmutable($function): self
853
    {
854 4
        $that = clone $this;
855
856
        /**
857
         * @psalm-suppress ImpureMethodCall - object is already cloned
858
         */
859 4
        $that->uasort($function);
860
861 4
        return $that;
862
    }
863
864
    /**
865
     * Sort the entries by keys using a user-defined comparison function.
866
     *
867
     * @param callable $function
868
     *
869
     * @throws \InvalidArgumentException
870
     *
871
     * @return static
872
     *                <p>(Mutable) Return this Arrayy object.</p>
873
     *
874
     * @psalm-return static<TKey,T>
875
     */
876 5
    public function uksort($function): self
877
    {
878 5
        return $this->customSortKeys($function);
879
    }
880
881
    /**
882
     * Sort the entries by keys using a user-defined comparison function.
883
     *
884
     * @param callable $function
885
     *
886
     * @throws \InvalidArgumentException
887
     *
888
     * @return static
889
     *                <p>(Immutable) Return this Arrayy object.</p>
890
     *
891
     * @psalm-return static<TKey,T>
892
     * @psalm-mutation-free
893
     */
894 1
    public function uksortImmutable($function): self
895
    {
896 1
        return $this->customSortKeysImmutable($function);
897
    }
898
899
    /**
900
     * Unserialize an string and return the instance of the "Arrayy"-class.
901
     *
902
     * @param string $string
903
     *
904
     * @return $this
905
     *
906
     * @psalm-return static<TKey,T>
907
     */
908 2
    public function unserialize($string): self
909
    {
910 2
        if (\PHP_VERSION_ID < 70400) {
911 2
            parent::unserialize($string);
912
913 2
            return $this;
914
        }
915
916
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
917
    }
918
919
    /**
920
     * Append a (key) + values to the current array.
921
     *
922
     * EXAMPLE: <code>
923
     * a(['fòô' => ['bàř']])->appendArrayValues(['foo1', 'foo2'], 'fòô'); // Arrayy['fòô' => ['bàř', 'foo1', 'foo2']]
924
     * </code>
925
     *
926
     * @param array $values
927
     * @param mixed $key
928
     *
929
     * @return $this
930
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
931
     *
932
     * @psalm-param  array<mixed,T> $values
933
     * @psalm-param  TKey|null $key
934
     * @psalm-return static<TKey,T>
935
     */
936 1
    public function appendArrayValues(array $values, $key = null)
937
    {
938 1
        $this->generatorToArray();
939
940 1
        if ($key !== null) {
941
            if (
942 1
                isset($this->array[$key])
943
                &&
944 1
                \is_array($this->array[$key]) === true
945
            ) {
946 1
                foreach ($values as $value) {
947 1
                    $this->array[$key][] = $value;
948
                }
949
            } else {
950 1
                foreach ($values as $value) {
951
                    $this->array[$key] = $value;
952
                }
953
            }
954
        } else {
955
            foreach ($values as $value) {
956
                $this->array[] = $value;
957
            }
958
        }
959
960 1
        return $this;
961
    }
962
963
    /**
964
     * Add a suffix to each key.
965
     *
966
     * @param mixed $prefix
967
     *
968
     * @return static
969
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
970
     *
971
     * @psalm-return static<TKey,T>
972
     * @psalm-mutation-free
973
     */
974 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...
975
    {
976
        // init
977 10
        $result = [];
978
979 10
        foreach ($this->getGenerator() as $key => $item) {
980 9
            if ($item instanceof self) {
981
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
982 9
            } elseif (\is_array($item) === true) {
983
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
984
                    ->appendToEachKey($prefix)
985
                    ->toArray();
986
            } else {
987 9
                $result[$prefix . $key] = $item;
988
            }
989
        }
990
991 10
        return self::create($result, $this->iteratorClass, false);
992
    }
993
994
    /**
995
     * Add a prefix to each value.
996
     *
997
     * @param mixed $prefix
998
     *
999
     * @return static
1000
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
1001
     *
1002
     * @psalm-return static<TKey,T>
1003
     * @psalm-mutation-free
1004
     */
1005 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...
1006
    {
1007
        // init
1008 10
        $result = [];
1009
1010 10
        foreach ($this->getGenerator() as $key => $item) {
1011 9
            if ($item instanceof self) {
1012
                $result[$key] = $item->appendToEachValue($prefix);
1013 9
            } elseif (\is_array($item) === true) {
1014
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
1015 9
            } elseif (\is_object($item) === true) {
1016 1
                $result[$key] = $item;
1017
            } else {
1018 8
                $result[$key] = $prefix . $item;
1019
            }
1020
        }
1021
1022 10
        return self::create($result, $this->iteratorClass, false);
1023
    }
1024
1025
    /**
1026
     * Sort an array in reverse order and maintain index association.
1027
     *
1028
     * @return $this
1029
     *               <p>(Mutable) Return this Arrayy object.</p>
1030
     *
1031
     * @psalm-return static<TKey,T>
1032
     */
1033 4
    public function arsort(): self
1034
    {
1035 4
        $this->generatorToArray();
1036
1037 4
        \arsort($this->array);
1038
1039 4
        return $this;
1040
    }
1041
1042
    /**
1043
     * Sort an array in reverse order and maintain index association.
1044
     *
1045
     * @return $this
1046
     *               <p>(Immutable) Return this Arrayy object.</p>
1047
     *
1048
     * @psalm-return static<TKey,T>
1049
     * @psalm-mutation-free
1050
     */
1051 10
    public function arsortImmutable(): self
1052
    {
1053 10
        $that = clone $this;
1054
1055 10
        $that->generatorToArray();
1056
1057 10
        \arsort($that->array);
1058
1059 10
        return $that;
1060
    }
1061
1062
    /**
1063
     * Iterate over the current array and execute a callback for each loop.
1064
     *
1065
     * EXAMPLE: <code>
1066
     * $result = A::create();
1067
     * $closure = function ($value, $key) use ($result) {
1068
     *     $result[$key] = ':' . $value . ':';
1069
     * };
1070
     * a(['foo', 'bar' => 'bis'])->at($closure); // Arrayy[':foo:', 'bar' => ':bis:']
1071
     * </code>
1072
     *
1073
     * @param \Closure $closure
1074
     *
1075
     * @return static
1076
     *                <p>(Immutable)</p>
1077
     *
1078
     * @psalm-return static<TKey,T>
1079
     * @psalm-mutation-free
1080
     */
1081 3
    public function at(\Closure $closure): self
1082
    {
1083 3
        $that = clone $this;
1084
1085 3
        foreach ($that->getGenerator() as $key => $value) {
1086 3
            $closure($value, $key);
1087
        }
1088
1089 3
        return static::create(
1090 3
            $that->toArray(),
1091 3
            $this->iteratorClass,
1092 3
            false
1093
        );
1094
    }
1095
1096
    /**
1097
     * Returns the average value of the current array.
1098
     *
1099
     * EXAMPLE: <code>
1100
     * a([-9, -8, -7, 1.32])->average(2); // -5.67
1101
     * </code>
1102
     *
1103
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
1104
     *
1105
     * @return float|int
1106
     *                   <p>The average value.</p>
1107
     * @psalm-mutation-free
1108
     */
1109 10
    public function average($decimals = 0)
1110
    {
1111 10
        $count = \count($this->toArray(), \COUNT_NORMAL);
1112
1113 10
        if (!$count) {
1114 2
            return 0;
1115
        }
1116
1117 8
        if ((int) $decimals !== $decimals) {
1118 3
            $decimals = 0;
1119
        }
1120
1121 8
        return \round(\array_sum($this->toArray()) / $count, $decimals);
1122
    }
1123
1124
    /**
1125
     * Changes all keys in an array.
1126
     *
1127
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
1128
     *                  or <strong>CASE_LOWER</strong> (default)</p>
1129
     *
1130
     * @return static
1131
     *                <p>(Immutable)</p>
1132
     *
1133
     * @psalm-return static<TKey,T>
1134
     * @psalm-mutation-free
1135
     */
1136 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
1137
    {
1138
        if (
1139 1
            $case !== \CASE_LOWER
1140
            &&
1141 1
            $case !== \CASE_UPPER
1142
        ) {
1143
            $case = \CASE_LOWER;
1144
        }
1145
1146 1
        $return = [];
1147 1
        foreach ($this->getGenerator() as $key => $value) {
1148 1
            \assert(\is_string($key) || \is_int($key) || \is_float($key));
1149
1150 1
            if ($case === \CASE_LOWER) {
1151 1
                $key = \mb_strtolower((string) $key);
1152
            } else {
1153 1
                $key = \mb_strtoupper((string) $key);
1154
            }
1155
1156 1
            $return[$key] = $value;
1157
        }
1158
1159 1
        return static::create(
1160 1
            $return,
1161 1
            $this->iteratorClass,
1162 1
            false
1163
        );
1164
    }
1165
1166
    /**
1167
     * Change the path separator of the array wrapper.
1168
     *
1169
     * By default, the separator is: "."
1170
     *
1171
     * @param string $separator <p>Separator to set.</p>
1172
     *
1173
     * @return $this
1174
     *               <p>(Mutable) Return this Arrayy object.</p>
1175
     *
1176
     * @psalm-return static<TKey,T>
1177
     */
1178 11
    public function changeSeparator($separator): self
1179
    {
1180 11
        $this->pathSeparator = $separator;
1181
1182 11
        return $this;
1183
    }
1184
1185
    /**
1186
     * Create a chunked version of the current array.
1187
     *
1188
     * EXAMPLE: <code>
1189
     * a([-9, -8, -7, 1.32])->chunk(2); // Arrayy[[-9, -8], [-7, 1.32]]
1190
     * </code>
1191
     *
1192
     * @param int  $size         <p>Size of each chunk.</p>
1193
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
1194
     *
1195
     * @return static
1196
     *                <p>(Immutable) A new array of chunks from the original array.</p>
1197
     *
1198
     * @psalm-return static<TKey,T>
1199
     * @psalm-mutation-free
1200
     */
1201 5
    public function chunk($size, $preserveKeys = false): self
1202
    {
1203 5
        return static::create(
1204 5
            \array_chunk($this->toArray(), $size, $preserveKeys),
1205 5
            $this->iteratorClass,
1206 5
            false
1207
        );
1208
    }
1209
1210
    /**
1211
     * Clean all falsy values from the current array.
1212
     *
1213
     * EXAMPLE: <code>
1214
     * a([-8 => -9, 1, 2 => false])->clean(); // Arrayy[-8 => -9, 1]
1215
     * </code>
1216
     *
1217
     * @return static
1218
     *                <p>(Immutable)</p>
1219
     *
1220
     * @psalm-return static<TKey,T>
1221
     * @psalm-mutation-free
1222
     */
1223 8
    public function clean(): self
1224
    {
1225 8
        return $this->filter(
1226
            static function ($value) {
1227 7
                return (bool) $value;
1228 8
            }
1229
        );
1230
    }
1231
1232
    /**
1233
     * WARNING!!! -> Clear the current full array or a $key of it.
1234
     *
1235
     * EXAMPLE: <code>
1236
     * a([-8 => -9, 1, 2 => false])->clear(); // Arrayy[]
1237
     * </code>
1238
     *
1239
     * @param int|int[]|string|string[]|null $key
1240
     *
1241
     * @return $this
1242
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1243
     *
1244
     * @psalm-return static<TKey,T>
1245
     */
1246 10
    public function clear($key = null): self
1247
    {
1248 10
        if ($key !== null) {
1249 3
            if (\is_array($key)) {
1250 1
                foreach ($key as $keyTmp) {
1251 1
                    $this->offsetUnset($keyTmp);
1252
                }
1253
            } else {
1254 2
                $this->offsetUnset($key);
1255
            }
1256
1257 3
            return $this;
1258
        }
1259
1260 7
        $this->array = [];
1261 7
        $this->generator = null;
1262
1263 7
        return $this;
1264
    }
1265
1266
    /**
1267
     * Check if an item is in the current array.
1268
     *
1269
     * EXAMPLE: <code>
1270
     * a([1, true])->contains(true); // true
1271
     * </code>
1272
     *
1273
     * @param float|int|string $value
1274
     * @param bool             $recursive
1275
     * @param bool             $strict
1276
     *
1277
     * @return bool
1278
     * @psalm-mutation-free
1279
     */
1280 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1281
    {
1282 23
        if ($recursive === true) {
1283 18
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1284
        }
1285
1286
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1287 14
        foreach ($this->getGeneratorByReference() as &$valueFromArray) {
1288 11
            if ($strict) {
1289 11
                if ($value === $valueFromArray) {
1290 11
                    return true;
1291
                }
1292
            } else {
1293
                /** @noinspection NestedPositiveIfStatementsInspection */
1294
                if ($value == $valueFromArray) {
1295
                    return true;
1296
                }
1297
            }
1298
        }
1299
1300 7
        return false;
1301
    }
1302
1303
    /**
1304
     * Check if an (case-insensitive) string is in the current array.
1305
     *
1306
     * EXAMPLE: <code>
1307
     * a(['E', 'é'])->containsCaseInsensitive('É'); // true
1308
     * </code>
1309
     *
1310
     * @param mixed $value
1311
     * @param bool  $recursive
1312
     *
1313
     * @return bool
1314
     * @psalm-mutation-free
1315
     *
1316
     * @psalm-suppress InvalidCast - hack for int|float|bool support
1317
     */
1318 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1319
    {
1320 26
        if ($value === null) {
1321 2
            return false;
1322
        }
1323
1324 24
        if ($recursive === true) {
1325
            /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1326 24
            foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1327 22
                if (\is_array($valueTmp) === true) {
1328 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1329 5
                    if ($return === true) {
1330 5
                        return $return;
1331
                    }
1332 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1333 16
                    return true;
1334
                }
1335
            }
1336
1337 8
            return false;
1338
        }
1339
1340
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1341 12
        foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1342 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1343 8
                return true;
1344
            }
1345
        }
1346
1347 4
        return false;
1348
    }
1349
1350
    /**
1351
     * Check if the given key/index exists in the array.
1352
     *
1353
     * EXAMPLE: <code>
1354
     * a([1 => true])->containsKey(1); // true
1355
     * </code>
1356
     *
1357
     * @param int|string $key <p>key/index to search for</p>
1358
     *
1359
     * @return bool
1360
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1361
     *
1362
     * @psalm-mutation-free
1363
     */
1364 4
    public function containsKey($key): bool
1365
    {
1366 4
        return $this->offsetExists($key);
1367
    }
1368
1369
    /**
1370
     * Check if all given needles are present in the array as key/index.
1371
     *
1372
     * EXAMPLE: <code>
1373
     * a([1 => true])->containsKeys(array(1 => 0)); // true
1374
     * </code>
1375
     *
1376
     * @param array $needles   <p>The keys you are searching for.</p>
1377
     * @param bool  $recursive
1378
     *
1379
     * @return bool
1380
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1381
     *
1382
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1383
     * @psalm-mutation-free
1384
     */
1385 2
    public function containsKeys(array $needles, $recursive = false): bool
1386
    {
1387 2
        if ($recursive === true) {
1388
            return
1389 2
                \count(
1390 2
                    \array_intersect(
1391 2
                        $needles,
1392 2
                        $this->keys(true)->toArray()
1393
                    ),
1394 2
                    \COUNT_RECURSIVE
1395
                )
1396
                ===
1397 2
                \count(
1398 2
                    $needles,
1399 2
                    \COUNT_RECURSIVE
1400
                );
1401
        }
1402
1403 1
        return \count(
1404 1
            \array_intersect($needles, $this->keys()->toArray()),
1405 1
            \COUNT_NORMAL
1406
        )
1407
               ===
1408 1
               \count(
1409 1
                   $needles,
1410 1
                   \COUNT_NORMAL
1411
               );
1412
    }
1413
1414
    /**
1415
     * Check if all given needles are present in the array as key/index.
1416
     *
1417
     * @param array $needles <p>The keys you are searching for.</p>
1418
     *
1419
     * @return bool
1420
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1421
     *
1422
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1423
     * @psalm-mutation-free
1424
     */
1425 1
    public function containsKeysRecursive(array $needles): bool
1426
    {
1427 1
        return $this->containsKeys($needles, true);
1428
    }
1429
1430
    /**
1431
     * alias: for "Arrayy->contains()"
1432
     *
1433
     * @param float|int|string $value
1434
     *
1435
     * @return bool
1436
     *
1437
     * @see Arrayy::contains()
1438
     * @psalm-mutation-free
1439
     */
1440 9
    public function containsValue($value): bool
1441
    {
1442 9
        return $this->contains($value);
1443
    }
1444
1445
    /**
1446
     * alias: for "Arrayy->contains($value, true)"
1447
     *
1448
     * @param float|int|string $value
1449
     *
1450
     * @return bool
1451
     *
1452
     * @see Arrayy::contains()
1453
     * @psalm-mutation-free
1454
     */
1455 18
    public function containsValueRecursive($value): bool
1456
    {
1457 18
        return $this->contains($value, true);
1458
    }
1459
1460
    /**
1461
     * Check if all given needles are present in the array.
1462
     *
1463
     * EXAMPLE: <code>
1464
     * a([1, true])->containsValues(array(1, true)); // true
1465
     * </code>
1466
     *
1467
     * @param array $needles
1468
     *
1469
     * @return bool
1470
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1471
     *
1472
     * @psalm-param array<mixed>|array<T> $needles
1473
     * @psalm-mutation-free
1474
     */
1475 1
    public function containsValues(array $needles): bool
1476
    {
1477 1
        return \count(\array_intersect($needles, $this->toArray()), \COUNT_NORMAL)
1478
               ===
1479 1
               \count($needles, \COUNT_NORMAL);
1480
    }
1481
1482
    /**
1483
     * Counts all the values of an array
1484
     *
1485
     * @see          http://php.net/manual/en/function.array-count-values.php
1486
     *
1487
     * @return static
1488
     *                <p>
1489
     *                (Immutable)
1490
     *                An associative Arrayy-object of values from input as
1491
     *                keys and their count as value.
1492
     *                </p>
1493
     *
1494
     * @psalm-return static<TKey,T>
1495
     * @psalm-mutation-free
1496
     */
1497 7
    public function countValues(): self
1498
    {
1499 7
        return self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1500
    }
1501
1502
    /**
1503
     * Creates an Arrayy object.
1504
     *
1505
     * @param mixed  $data
1506
     * @param string $iteratorClass
1507
     * @param bool   $checkPropertiesInConstructor
1508
     *
1509
     * @return static
1510
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1511
     *
1512
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1513
     *
1514
     * @psalm-mutation-free
1515
     */
1516 718
    public static function create(
1517
        $data = [],
1518
        string $iteratorClass = ArrayyIterator::class,
1519
        bool $checkPropertiesInConstructor = true
1520
    ) {
1521 718
        return new static(
1522 718
            $data,
1523 718
            $iteratorClass,
1524 718
            $checkPropertiesInConstructor
1525
        );
1526
    }
1527
1528
    /**
1529
     * Flatten an array with the given character as a key delimiter
1530
     *
1531
     * @param string     $delimiter
1532
     * @param string     $prepend
1533
     * @param array|null $items
1534
     *
1535
     * @return array
1536
     */
1537 2
    public function flatten($delimiter = '.', $prepend = '', $items = null)
1538
    {
1539
        // init
1540 2
        $flatten = [];
1541
1542 2
        if ($items === null) {
1543 2
            $items = $this->array;
1544
        }
1545
1546 2
        foreach ($items as $key => $value) {
1547 2
            if (\is_array($value) && !empty($value)) {
1548 2
                $flatten[] = $this->flatten($delimiter, $prepend . $key . $delimiter, $value);
1549
            } else {
1550 2
                $flatten[] = [$prepend . $key => $value];
1551
            }
1552
        }
1553
1554 2
        if (\count($flatten) === 0) {
1555
            return [];
1556
        }
1557
1558 2
        return \array_merge_recursive([], ...$flatten);
1559
    }
1560
1561
    /**
1562
     * WARNING: Creates an Arrayy object by reference.
1563
     *
1564
     * @param array $array
1565
     *
1566
     * @return $this
1567
     *               <p>(Mutable) Return this Arrayy object.</p>
1568
     *
1569
     * @psalm-param  array<mixed,mixed>|array<array-key,mixed> $array
1570
     */
1571 27
    public function createByReference(array &$array = []): self
1572
    {
1573 27
        $array = $this->fallbackForArray($array);
1574
1575 27
        $this->array = &$array;
1576
1577 27
        return $this;
1578
    }
1579
1580
    /**
1581
     * Create an new instance from a callable function which will return an Generator.
1582
     *
1583
     * @param callable $generatorFunction
1584
     *
1585
     * @return static
1586
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1587
     *
1588
     * @psalm-param callable():\Generator<array-key,mixed> $generatorFunction
1589
     *
1590
     * @psalm-mutation-free
1591
     */
1592 7
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1593
    {
1594 7
        return self::create($generatorFunction);
1595
    }
1596
1597
    /**
1598
     * Create an new instance filled with a copy of values from a "Generator"-object.
1599
     *
1600
     * @param \Generator $generator
1601
     *
1602
     * @return static
1603
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1604
     *
1605
     * @psalm-param \Generator<array-key,mixed> $generator
1606
     *
1607
     * @psalm-mutation-free
1608
     */
1609 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1610
    {
1611 4
        return self::create(\iterator_to_array($generator, true));
1612
    }
1613
1614
    /**
1615
     * Create an new Arrayy object via JSON.
1616
     *
1617
     * @param string $json
1618
     *
1619
     * @return static
1620
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1621
     *
1622
     * @psalm-mutation-free
1623
     */
1624 5
    public static function createFromJson(string $json): self
1625
    {
1626 5
        return static::create(\json_decode($json, true));
1627
    }
1628
1629
    /**
1630
     * Create an new Arrayy object via JSON.
1631
     *
1632
     * @param array $array
1633
     *
1634
     * @return static
1635
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1636
     *
1637
     * @psalm-mutation-free
1638
     */
1639 1
    public static function createFromArray(array $array): self
1640
    {
1641 1
        return static::create($array);
1642
    }
1643
1644
    /**
1645
     * Create an new instance filled with values from an object that is iterable.
1646
     *
1647
     * @param \Traversable $object <p>iterable object</p>
1648
     *
1649
     * @return static
1650
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1651
     *
1652
     * @psalm-param \Traversable<array-key,mixed> $object
1653
     *
1654
     * @psalm-mutation-free
1655
     */
1656 4
    public static function createFromObject(\Traversable $object): self
1657
    {
1658
        // init
1659 4
        $arrayy = new static();
1660
1661 4
        if ($object instanceof self) {
1662 4
            $objectArray = $object->getGenerator();
1663
        } else {
1664
            $objectArray = $object;
1665
        }
1666
1667 4
        foreach ($objectArray as $key => $value) {
1668
            /**
1669
             * @psalm-suppress ImpureMethodCall - object is already re-created
1670
             */
1671 3
            $arrayy->internalSet($key, $value);
1672
        }
1673
1674 4
        return $arrayy;
1675
    }
1676
1677
    /**
1678
     * Create an new instance filled with values from an object.
1679
     *
1680
     * @param object $object
1681
     *
1682
     * @return static
1683
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1684
     *
1685
     * @psalm-mutation-free
1686
     */
1687 5
    public static function createFromObjectVars($object): self
1688
    {
1689 5
        return self::create(self::objectToArray($object));
1690
    }
1691
1692
    /**
1693
     * Create an new Arrayy object via string.
1694
     *
1695
     * @param string      $str       <p>The input string.</p>
1696
     * @param string|null $delimiter <p>The boundary string.</p>
1697
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1698
     *                               used.</p>
1699
     *
1700
     * @return static
1701
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1702
     *
1703
     * @psalm-mutation-free
1704
     */
1705 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1706
    {
1707 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...
1708 1
            \preg_match_all($regEx, $str, $array);
1709
1710 1
            if (!empty($array)) {
1711 1
                $array = $array[0];
1712
            }
1713
        } else {
1714
            /** @noinspection NestedPositiveIfStatementsInspection */
1715 9
            if ($delimiter !== null) {
1716 7
                $array = \explode($delimiter, $str);
1717
            } else {
1718 2
                $array = [$str];
1719
            }
1720
        }
1721
1722
        // trim all string in the array
1723
        /**
1724
         * @psalm-suppress MissingClosureParamType
1725
         */
1726 10
        \array_walk(
1727 10
            $array,
1728
            static function (&$val) {
1729 10
                if ((string) $val === $val) {
1730 10
                    $val = \trim($val);
1731
                }
1732 10
            }
1733
        );
1734
1735 10
        return static::create($array);
1736
    }
1737
1738
    /**
1739
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1740
     *
1741
     * @param \Traversable $traversable
1742
     *
1743
     * @return static
1744
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1745
     *
1746
     * @psalm-param \Traversable<array-key,mixed> $traversable
1747
     *
1748
     * @psalm-mutation-free
1749
     */
1750 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1751
    {
1752 1
        return self::create(\iterator_to_array($traversable, true));
1753
    }
1754
1755
    /**
1756
     * Create an new instance containing a range of elements.
1757
     *
1758
     * @param float|int|string $low  <p>First value of the sequence.</p>
1759
     * @param float|int|string $high <p>The sequence is ended upon reaching the end value.</p>
1760
     * @param float|int        $step <p>Used as the increment between elements in the sequence.</p>
1761
     *
1762
     * @return static
1763
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1764
     *
1765
     * @psalm-mutation-free
1766
     */
1767 2
    public static function createWithRange($low, $high, $step = 1): self
1768
    {
1769 2
        return static::create(\range($low, $high, $step));
1770
    }
1771
1772
    /**
1773
     * Gets the element of the array at the current internal iterator position.
1774
     *
1775
     * @return false|mixed
1776
     */
1777
    public function current()
1778
    {
1779
        return \current($this->array);
1780
    }
1781
1782
    /**
1783
     * Custom sort by index via "uksort".
1784
     *
1785
     * EXAMPLE: <code>
1786
     * $callable = function ($a, $b) {
1787
     *     if ($a == $b) {
1788
     *         return 0;
1789
     *     }
1790
     *     return ($a > $b) ? 1 : -1;
1791
     * };
1792
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1793
     * $resultArrayy = $arrayy->customSortKeys($callable); // Arrayy['one' => 1, 'three' => 3, 'two' => 2]
1794
     * </code>
1795
     *
1796
     * @see          http://php.net/manual/en/function.uksort.php
1797
     *
1798
     * @param callable $function
1799
     *
1800
     * @throws \InvalidArgumentException
1801
     *
1802
     * @return $this
1803
     *               <p>(Mutable) Return this Arrayy object.</p>
1804
     *
1805
     * @psalm-return static<TKey,T>
1806
     */
1807 5
    public function customSortKeys(callable $function): self
1808
    {
1809 5
        $this->generatorToArray();
1810
1811 5
        \uksort($this->array, $function);
1812
1813 5
        return $this;
1814
    }
1815
1816
    /**
1817
     * Custom sort by index via "uksort".
1818
     *
1819
     * @see          http://php.net/manual/en/function.uksort.php
1820
     *
1821
     * @param callable $function
1822
     *
1823
     * @throws \InvalidArgumentException
1824
     *
1825
     * @return $this
1826
     *               <p>(Immutable) Return this Arrayy object.</p>
1827
     *
1828
     * @psalm-return static<TKey,T>
1829
     * @psalm-mutation-free
1830
     */
1831 1
    public function customSortKeysImmutable(callable $function): self
1832
    {
1833 1
        $that = clone $this;
1834
1835 1
        $that->generatorToArray();
1836
1837
        /**
1838
         * @psalm-suppress ImpureFunctionCall - object is already cloned
1839
         */
1840 1
        \uksort($that->array, $function);
1841
1842 1
        return $that;
1843
    }
1844
1845
    /**
1846
     * Custom sort by value via "usort".
1847
     *
1848
     * EXAMPLE: <code>
1849
     * $callable = function ($a, $b) {
1850
     *     if ($a == $b) {
1851
     *         return 0;
1852
     *     }
1853
     *     return ($a > $b) ? 1 : -1;
1854
     * };
1855
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1856
     * $resultArrayy = $arrayy->customSortValues($callable); // Arrayy['one' => 1, 'two' => 2, 'three' => 3]
1857
     * </code>
1858
     *
1859
     * @see          http://php.net/manual/en/function.usort.php
1860
     *
1861
     * @param callable $function
1862
     *
1863
     * @throws \InvalidArgumentException
1864
     *
1865
     * @return $this
1866
     *               <p>(Mutable) Return this Arrayy object.</p>
1867
     *
1868
     * @psalm-return static<TKey,T>
1869
     */
1870 10 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...
1871
    {
1872 10
        if (\is_callable($function) === false) {
1873
            throw new \InvalidArgumentException('Passed function must be callable');
1874
        }
1875
1876 10
        $this->generatorToArray();
1877
1878 10
        \usort($this->array, $function);
1879
1880 10
        return $this;
1881
    }
1882
1883
    /**
1884
     * Custom sort by value via "usort".
1885
     *
1886
     * @see          http://php.net/manual/en/function.usort.php
1887
     *
1888
     * @param callable $function
1889
     *
1890
     * @throws \InvalidArgumentException
1891
     *
1892
     * @return $this
1893
     *               <p>(Immutable) Return this Arrayy object.</p>
1894
     *
1895
     * @psalm-return static<TKey,T>
1896
     * @psalm-mutation-free
1897
     */
1898 4
    public function customSortValuesImmutable($function): self
1899
    {
1900 4
        $that = clone $this;
1901
1902
        /**
1903
         * @psalm-suppress ImpureMethodCall - object is already cloned
1904
         */
1905 4
        $that->customSortValues($function);
1906
1907 4
        return $that;
1908
    }
1909
1910
    /**
1911
     * Delete the given key or keys.
1912
     *
1913
     * @param int|int[]|string|string[] $keyOrKeys
1914
     *
1915
     * @return void
1916
     */
1917 9
    public function delete($keyOrKeys)
1918
    {
1919 9
        $keyOrKeys = (array) $keyOrKeys;
1920
1921 9
        foreach ($keyOrKeys as $key) {
1922 9
            $this->offsetUnset($key);
1923
        }
1924 9
    }
1925
1926
    /**
1927
     * Return values that are only in the current array.
1928
     *
1929
     * EXAMPLE: <code>
1930
     * a([1 => 1, 2 => 2])->diff([1 => 1]); // Arrayy[2 => 2]
1931
     * </code>
1932
     *
1933
     * @param array ...$array
1934
     *
1935
     * @return static
1936
     *                <p>(Immutable)</p>
1937
     *
1938
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1939
     * @psalm-return static<TKey,T>
1940
     * @psalm-mutation-free
1941
     */
1942 13
    public function diff(...$array): self
1943
    {
1944 13
        return static::create(
1945 13
            \array_diff($this->toArray(), ...$array),
1946 13
            $this->iteratorClass,
1947 13
            false
1948
        );
1949
    }
1950
1951
    /**
1952
     * Return values that are only in the current array.
1953
     *
1954
     * @param array ...$array
1955
     *
1956
     * @return static
1957
     *                <p>(Immutable)</p>
1958
     *
1959
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1960
     * @psalm-return static<TKey,T>
1961
     * @psalm-mutation-free
1962
     */
1963 8
    public function diffKey(...$array): self
1964
    {
1965 8
        return static::create(
1966 8
            \array_diff_key($this->toArray(), ...$array),
1967 8
            $this->iteratorClass,
1968 8
            false
1969
        );
1970
    }
1971
1972
    /**
1973
     * Return values and Keys that are only in the current array.
1974
     *
1975
     * @param array $array
1976
     *
1977
     * @return static
1978
     *                <p>(Immutable)</p>
1979
     *
1980
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1981
     * @psalm-return static<TKey,T>
1982
     * @psalm-mutation-free
1983
     */
1984 8
    public function diffKeyAndValue(array $array = []): self
1985
    {
1986 8
        return static::create(
1987 8
            \array_diff_assoc($this->toArray(), $array),
1988 8
            $this->iteratorClass,
1989 8
            false
1990
        );
1991
    }
1992
1993
    /**
1994
     * Return values that are only in the current multi-dimensional array.
1995
     *
1996
     * @param array                 $array
1997
     * @param array|\Generator|null $helperVariableForRecursion <p>(only for internal usage)</p>
1998
     *
1999
     * @return static
2000
     *                <p>(Immutable)</p>
2001
     *
2002
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2003
     * @psalm-param  null|array<TKey,T>|\Generator<TKey,T> $helperVariableForRecursion
2004
     * @psalm-return static<TKey,T>
2005
     * @psalm-mutation-free
2006
     */
2007 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
2008
    {
2009
        // init
2010 1
        $result = [];
2011
2012
        if (
2013 1
            $helperVariableForRecursion !== null
2014
            &&
2015 1
            \is_array($helperVariableForRecursion) === true
2016
        ) {
2017
            $arrayForTheLoop = $helperVariableForRecursion;
2018
        } else {
2019 1
            $arrayForTheLoop = $this->getGenerator();
2020
        }
2021
2022 1
        foreach ($arrayForTheLoop as $key => $value) {
2023 1
            if ($value instanceof self) {
2024
                $value = $value->toArray();
2025
            }
2026
2027 1
            if (\array_key_exists($key, $array)) {
2028 1
                if ($value !== $array[$key]) {
2029 1
                    $result[$key] = $value;
2030
                }
2031
            } else {
2032 1
                $result[$key] = $value;
2033
            }
2034
        }
2035
2036 1
        return static::create(
2037 1
            $result,
2038 1
            $this->iteratorClass,
2039 1
            false
2040
        );
2041
    }
2042
2043
    /**
2044
     * Return values that are only in the new $array.
2045
     *
2046
     * @param array $array
2047
     *
2048
     * @return static
2049
     *                <p>(Immutable)</p>
2050
     *
2051
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2052
     * @psalm-return static<TKey,T>
2053
     * @psalm-mutation-free
2054
     */
2055 8
    public function diffReverse(array $array = []): self
2056
    {
2057 8
        return static::create(
2058 8
            \array_diff($array, $this->toArray()),
2059 8
            $this->iteratorClass,
2060 8
            false
2061
        );
2062
    }
2063
2064
    /**
2065
     * Divide an array into two arrays. One with keys and the other with values.
2066
     *
2067
     * @return static
2068
     *                <p>(Immutable)</p>
2069
     *
2070
     * @psalm-return static<TKey,T>
2071
     * @psalm-mutation-free
2072
     */
2073 1
    public function divide(): self
2074
    {
2075 1
        return static::create(
2076
            [
2077 1
                $this->keys(),
2078 1
                $this->values(),
2079
            ],
2080 1
            $this->iteratorClass,
2081 1
            false
2082
        );
2083
    }
2084
2085
    /**
2086
     * Iterate over the current array and modify the array's value.
2087
     *
2088
     * @param \Closure $closure
2089
     *
2090
     * @return static
2091
     *                <p>(Immutable)</p>
2092
     *
2093
     * @psalm-return static<TKey,T>
2094
     * @psalm-mutation-free
2095
     */
2096 5 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...
2097
    {
2098
        // init
2099 5
        $array = [];
2100
2101 5
        foreach ($this->getGenerator() as $key => $value) {
2102 5
            $array[$key] = $closure($value, $key);
2103
        }
2104
2105 5
        return static::create(
2106 5
            $array,
2107 5
            $this->iteratorClass,
2108 5
            false
2109
        );
2110
    }
2111
2112
    /**
2113
     * Sets the internal iterator to the last element in the array and returns this element.
2114
     *
2115
     * @return mixed
2116
     */
2117
    public function end()
2118
    {
2119
        return \end($this->array);
2120
    }
2121
2122
    /**
2123
     * Check if a value is in the current array using a closure.
2124
     *
2125
     * @param \Closure $closure
2126
     *
2127
     * @return bool
2128
     *              <p>Returns true if the given value is found, false otherwise.</p>
2129
     */
2130 4
    public function exists(\Closure $closure): bool
2131
    {
2132
        // init
2133 4
        $isExists = false;
2134
2135 4
        foreach ($this->getGenerator() as $key => $value) {
2136 3
            if ($closure($value, $key)) {
2137 1
                $isExists = true;
2138
2139 1
                break;
2140
            }
2141
        }
2142
2143 4
        return $isExists;
2144
    }
2145
2146
    /**
2147
     * Fill the array until "$num" with "$default" values.
2148
     *
2149
     * @param int   $num
2150
     * @param mixed $default
2151
     *
2152
     * @return static
2153
     *                <p>(Immutable)</p>
2154
     *
2155
     * @psalm-return static<TKey,T>
2156
     * @psalm-mutation-free
2157
     */
2158 8
    public function fillWithDefaults(int $num, $default = null): self
2159
    {
2160 8
        if ($num < 0) {
2161 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
2162
        }
2163
2164 7
        $this->generatorToArray();
2165
2166 7
        $tmpArray = $this->array;
2167
2168 7
        $count = \count($tmpArray);
2169
2170 7
        while ($count < $num) {
2171 4
            $tmpArray[] = $default;
2172 4
            ++$count;
2173
        }
2174
2175 7
        return static::create(
2176 7
            $tmpArray,
2177 7
            $this->iteratorClass,
2178 7
            false
2179
        );
2180
    }
2181
2182
    /**
2183
     * Find all items in an array that pass the truth test.
2184
     *
2185
     * @param \Closure|null $closure [optional] <p>
2186
     *                               The callback function to use
2187
     *                               </p>
2188
     *                               <p>
2189
     *                               If no callback is supplied, all entries of
2190
     *                               input equal to false (see
2191
     *                               converting to
2192
     *                               boolean) will be removed.
2193
     *                               </p>
2194
     * @param int           $flag    [optional] <p>
2195
     *                               Flag determining what arguments are sent to <i>callback</i>:
2196
     *                               </p><ul>
2197
     *                               <li>
2198
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
2199
     *                               to <i>callback</i> instead of the value</span>
2200
     *                               </li>
2201
     *                               <li>
2202
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
2203
     *                               arguments to <i>callback</i> instead of the value</span>
2204
     *                               </li>
2205
     *                               </ul>
2206
     *
2207
     * @return static
2208
     *                <p>(Immutable)</p>
2209
     *
2210
     * @psalm-param \Closure(T=,TKey=):bool|\Closure(T=):bool $closure
2211
     * @psalm-return static<TKey,T>
2212
     * @psalm-mutation-free
2213
     */
2214 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
2215
    {
2216 12
        if (!$closure) {
2217 1
            return $this->clean();
2218
        }
2219
2220 12
        return static::create(
2221 12
            \array_filter($this->toArray(), $closure, $flag),
2222 12
            $this->iteratorClass,
2223 12
            false
2224
        );
2225
    }
2226
2227
    /**
2228
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
2229
     * property within that.
2230
     *
2231
     * @param string          $property
2232
     * @param string|string[] $value
2233
     * @param string          $comparisonOp
2234
     *                                      <p>
2235
     *                                      'eq' (equals),<br />
2236
     *                                      'gt' (greater),<br />
2237
     *                                      'gte' || 'ge' (greater or equals),<br />
2238
     *                                      'lt' (less),<br />
2239
     *                                      'lte' || 'le' (less or equals),<br />
2240
     *                                      'ne' (not equals),<br />
2241
     *                                      'contains',<br />
2242
     *                                      'notContains',<br />
2243
     *                                      'newer' (via strtotime),<br />
2244
     *                                      'older' (via strtotime),<br />
2245
     *                                      </p>
2246
     *
2247
     * @return static
2248
     *                <p>(Immutable)</p>
2249
     *
2250
     * @psalm-return static<TKey,T>
2251
     * @psalm-mutation-free
2252
     *
2253
     * @psalm-suppress MissingClosureReturnType
2254
     * @psalm-suppress MissingClosureParamType
2255
     */
2256 1
    public function filterBy(
2257
        string $property,
2258
        $value,
2259
        string $comparisonOp = null
2260
    ): self {
2261 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...
2262 1
            $comparisonOp = \is_array($value) === true ? 'contains' : 'eq';
2263
        }
2264
2265
        $ops = [
2266
            'eq' => static function ($item, $prop, $value): bool {
2267 1
                return $item[$prop] === $value;
2268 1
            },
2269
            'gt' => static function ($item, $prop, $value): bool {
2270
                return $item[$prop] > $value;
2271 1
            },
2272
            'ge' => static function ($item, $prop, $value): bool {
2273
                return $item[$prop] >= $value;
2274 1
            },
2275
            'gte' => static function ($item, $prop, $value): bool {
2276
                return $item[$prop] >= $value;
2277 1
            },
2278
            'lt' => static function ($item, $prop, $value): bool {
2279 1
                return $item[$prop] < $value;
2280 1
            },
2281
            'le' => static function ($item, $prop, $value): bool {
2282
                return $item[$prop] <= $value;
2283 1
            },
2284
            'lte' => static function ($item, $prop, $value): bool {
2285
                return $item[$prop] <= $value;
2286 1
            },
2287
            'ne' => static function ($item, $prop, $value): bool {
2288
                return $item[$prop] !== $value;
2289 1
            },
2290
            'contains' => static function ($item, $prop, $value): bool {
2291 1
                return \in_array($item[$prop], (array) $value, true);
2292 1
            },
2293
            'notContains' => static function ($item, $prop, $value): bool {
2294
                return !\in_array($item[$prop], (array) $value, true);
2295 1
            },
2296
            'newer' => static function ($item, $prop, $value): bool {
2297
                return \strtotime($item[$prop]) > \strtotime($value);
2298 1
            },
2299
            'older' => static function ($item, $prop, $value): bool {
2300
                return \strtotime($item[$prop]) < \strtotime($value);
2301 1
            },
2302
        ];
2303
2304 1
        $result = \array_values(
2305 1
            \array_filter(
2306 1
                $this->toArray(false, true),
2307
                static function ($item) use (
2308 1
                    $property,
2309 1
                    $value,
2310 1
                    $ops,
2311 1
                    $comparisonOp
2312
                ) {
2313 1
                    $item = (array) $item;
2314 1
                    $itemArrayy = static::create($item);
2315 1
                    $item[$property] = $itemArrayy->get($property, []);
2316
2317 1
                    return $ops[$comparisonOp]($item, $property, $value);
2318 1
                }
2319
            )
2320
        );
2321
2322 1
        return static::create(
2323 1
            $result,
2324 1
            $this->iteratorClass,
2325 1
            false
2326
        );
2327
    }
2328
2329
    /**
2330
     * Find the first item in an array that passes the truth test,
2331
     *  otherwise return false
2332
     *
2333
     * @param \Closure $closure
2334
     *
2335
     * @return false|mixed
2336
     *                     <p>Return false if we did not find the value.</p>
2337
     */
2338 8 View Code Duplication
    public function find(\Closure $closure)
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...
2339
    {
2340 8
        foreach ($this->getGenerator() as $key => $value) {
2341 6
            if ($closure($value, $key)) {
2342 5
                return $value;
2343
            }
2344
        }
2345
2346 3
        return false;
2347
    }
2348
2349
    /**
2350
     * find by ...
2351
     *
2352
     * @param string          $property
2353
     * @param string|string[] $value
2354
     * @param string          $comparisonOp
2355
     *
2356
     * @return static
2357
     *                <p>(Immutable)</p>
2358
     *
2359
     * @psalm-return static<TKey,T>
2360
     * @psalm-mutation-free
2361
     */
2362 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
2363
    {
2364 1
        return $this->filterBy($property, $value, $comparisonOp);
2365
    }
2366
2367
    /**
2368
     * Get the first value from the current array.
2369
     *
2370
     * @return mixed
2371
     *               <p>Return null if there wasn't a element.</p>
2372
     */
2373 22
    public function first()
2374
    {
2375 22
        $key_first = $this->firstKey();
2376 22
        if ($key_first === null) {
2377 3
            return null;
2378
        }
2379
2380 19
        return $this->get($key_first);
2381
    }
2382
2383
    /**
2384
     * Get the first key from the current array.
2385
     *
2386
     * @return mixed
2387
     *               <p>Return null if there wasn't a element.</p>
2388
     * @psalm-mutation-free
2389
     */
2390 29
    public function firstKey()
2391
    {
2392 29
        $this->generatorToArray();
2393
2394 29
        return \array_key_first($this->array);
2395
    }
2396
2397
    /**
2398
     * Get the first value(s) from the current array.
2399
     * And will return an empty array if there was no first entry.
2400
     *
2401
     * @param int|null $number <p>How many values you will take?</p>
2402
     *
2403
     * @return static
2404
     *                <p>(Immutable)</p>
2405
     *
2406
     * @psalm-return static<TKey,T>
2407
     * @psalm-mutation-free
2408
     */
2409 37 View Code Duplication
    public function firstsImmutable(int $number = null): 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...
2410
    {
2411 37
        $arrayTmp = $this->toArray();
2412
2413 37
        if ($number === null) {
2414 14
            $array = (array) \array_shift($arrayTmp);
2415
        } else {
2416 23
            $array = \array_splice($arrayTmp, 0, $number);
2417
        }
2418
2419 37
        return static::create(
2420 37
            $array,
2421 37
            $this->iteratorClass,
2422 37
            false
2423
        );
2424
    }
2425
2426
    /**
2427
     * Get the first value(s) from the current array.
2428
     * And will return an empty array if there was no first entry.
2429
     *
2430
     * @param int|null $number <p>How many values you will take?</p>
2431
     *
2432
     * @return static
2433
     *                <p>(Immutable)</p>
2434
     *
2435
     * @psalm-return static<TKey,T>
2436
     * @psalm-mutation-free
2437
     */
2438 3 View Code Duplication
    public function firstsKeys(int $number = null): 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...
2439
    {
2440 3
        $arrayTmp = $this->keys()->toArray();
2441
2442 3
        if ($number === null) {
2443
            $array = (array) \array_shift($arrayTmp);
2444
        } else {
2445 3
            $array = \array_splice($arrayTmp, 0, $number);
2446
        }
2447
2448 3
        return static::create(
2449 3
            $array,
2450 3
            $this->iteratorClass,
2451 3
            false
2452
        );
2453
    }
2454
2455
    /**
2456
     * Get and rmove the first value(s) from the current array.
2457
     * And will return an empty array if there was no first entry.
2458
     *
2459
     * @param int|null $number <p>How many values you will take?</p>
2460
     *
2461
     * @return $this
2462
     *               <p>(Mutable)</p>
2463
     *
2464
     * @psalm-return static<TKey,T>
2465
     */
2466 34
    public function firstsMutable(int $number = null): self
2467
    {
2468 34
        $this->generatorToArray();
2469
2470 34
        if ($number === null) {
2471 19
            $this->array = (array) \array_shift($this->array);
2472
        } else {
2473 15
            $this->array = \array_splice($this->array, 0, $number);
2474
        }
2475
2476 34
        return $this;
2477
    }
2478
2479
    /**
2480
     * Exchanges all keys with their associated values in an array.
2481
     *
2482
     * @return static
2483
     *                <p>(Immutable)</p>
2484
     *
2485
     * @psalm-return static<TKey,T>
2486
     * @psalm-mutation-free
2487
     */
2488 1
    public function flip(): self
2489
    {
2490 1
        return static::create(
2491 1
            \array_flip($this->toArray()),
2492 1
            $this->iteratorClass,
2493 1
            false
2494
        );
2495
    }
2496
2497
    /**
2498
     * Get a value from an array (optional using dot-notation).
2499
     *
2500
     * @param mixed $key            <p>The key to look for.</p>
2501
     * @param mixed $fallback       <p>Value to fallback to.</p>
2502
     * @param array $array          <p>The array to get from, if it's set to "null" we use the current array from the
2503
     *                              class.</p>
2504
     * @param bool  $useByReference
2505
     *
2506
     * @return mixed|static
2507
     *
2508
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
2509
     * @psalm-mutation-free
2510
     */
2511 242
    public function get(
2512
        $key,
2513
        $fallback = null,
2514
        array $array = null,
2515
        bool $useByReference = false
2516
    ) {
2517 242
        if ($array !== null) {
2518 4
            if ($useByReference) {
2519
                $usedArray = &$array;
2520
            } else {
2521 4
                $usedArray = $array;
2522
            }
2523
        } else {
2524 239
            $this->generatorToArray();
2525
2526 239
            if ($useByReference) {
2527 126
                $usedArray = &$this->array;
2528
            } else {
2529 131
                $usedArray = $this->array;
2530
            }
2531
        }
2532
2533 242
        if ($key === null) {
2534 1
            return static::create(
2535 1
                [],
2536 1
                $this->iteratorClass,
2537 1
                false
2538 1
            )->createByReference($usedArray);
2539
        }
2540
2541
        // php cast "bool"-index into "int"-index
2542 242
        if ((bool) $key === $key) {
2543 3
            $key = (int) $key;
2544
        }
2545
2546 242
        if (\array_key_exists($key, $usedArray) === true) {
2547 204
            if (\is_array($usedArray[$key]) === true) {
2548 19
                return static::create(
2549 19
                    [],
2550 19
                    $this->iteratorClass,
2551 19
                    false
2552 19
                )->createByReference($usedArray[$key]);
2553
            }
2554
2555 190
            return $usedArray[$key];
2556
        }
2557
2558
        // crawl through array, get key according to object or not
2559 61
        $usePath = false;
2560
        if (
2561 61
            $this->pathSeparator
2562
            &&
2563 61
            (string) $key === $key
2564
            &&
2565 61
            \strpos($key, $this->pathSeparator) !== false
2566
        ) {
2567 31
            $segments = \explode($this->pathSeparator, (string) $key);
2568 31
            if ($segments !== false) {
2569 31
                $usePath = true;
2570 31
                $usedArrayTmp = $usedArray; // do not use the reference for dot-annotations
2571
2572 31
                foreach ($segments as $segment) {
2573
                    if (
2574
                        (
2575 31
                            \is_array($usedArrayTmp) === true
2576
                            ||
2577 31
                            $usedArrayTmp instanceof \ArrayAccess
2578
                        )
2579
                        &&
2580 31
                        isset($usedArrayTmp[$segment])
2581
                    ) {
2582 30
                        $usedArrayTmp = $usedArrayTmp[$segment];
2583
2584 30
                        continue;
2585
                    }
2586
2587
                    if (
2588 14
                        \is_object($usedArrayTmp) === true
2589
                        &&
2590 14
                        \property_exists($usedArrayTmp, $segment)
2591
                    ) {
2592 1
                        $usedArrayTmp = $usedArrayTmp->{$segment};
2593
2594 1
                        continue;
2595
                    }
2596
2597 13
                    if (isset($segments[0]) && $segments[0] === '*') {
2598 1
                        $segmentsTmp = $segments;
2599 1
                        unset($segmentsTmp[0]);
2600 1
                        $keyTmp = \implode('.', $segmentsTmp);
2601 1
                        $returnTmp = static::create(
2602 1
                            [],
2603 1
                            $this->iteratorClass,
2604 1
                            false
2605
                        );
2606 1
                        foreach ($this->getAll() as $dataTmp) {
2607 1
                            if ($dataTmp instanceof self) {
2608
                                $returnTmp->add($dataTmp->get($keyTmp));
2609
2610
                                continue;
2611
                            }
2612
2613
                            if (
2614
                                (
2615 1
                                    \is_array($dataTmp) === true
2616
                                    ||
2617 1
                                    $dataTmp instanceof \ArrayAccess
2618
                                )
2619
                                &&
2620 1
                                isset($dataTmp[$keyTmp])
2621
                            ) {
2622
                                $returnTmp->add($dataTmp[$keyTmp]);
2623
2624
                                continue;
2625
                            }
2626
2627
                            if (
2628 1
                                \is_object($dataTmp) === true
2629
                                &&
2630 1
                                \property_exists($dataTmp, $keyTmp)
2631
                            ) {
2632 1
                                $returnTmp->add($dataTmp->{$keyTmp});
2633
2634
                                /** @noinspection UnnecessaryContinueInspection */
2635 1
                                continue;
2636
                            }
2637
                        }
2638
2639 1
                        if ($returnTmp->count() > 0) {
2640 1
                            return $returnTmp;
2641
                        }
2642
                    }
2643
2644 12
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2645
                }
2646
            }
2647
        }
2648
2649 58
        if (isset($usedArrayTmp)) {
2650 28 View Code Duplication
            if (!$usePath && !isset($usedArrayTmp[$key])) {
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...
2651
                return $fallback instanceof \Closure ? $fallback() : $fallback;
2652
            }
2653
2654 28
            if (\is_array($usedArrayTmp) === true) {
2655 6
                return static::create(
2656 6
                    [],
2657 6
                    $this->iteratorClass,
2658 6
                    false
2659 6
                )->createByReference($usedArrayTmp);
2660
            }
2661
2662 28
            return $usedArrayTmp;
2663
2664
        }
2665
2666 30 View Code Duplication
        if (!$usePath && !isset($usedArray[$key])) {
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...
2667 30
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2668
        }
2669
2670
        return static::create(
2671
            [],
2672
            $this->iteratorClass,
2673
            false
2674
        )->createByReference($usedArray);
2675
    }
2676
2677
    /**
2678
     * alias: for "Arrayy->toArray()"
2679
     *
2680
     * @return array
2681
     *
2682
     * @see          Arrayy::getArray()
2683
     *
2684
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2685
     */
2686 15
    public function getAll(): array
2687
    {
2688 15
        return $this->toArray();
2689
    }
2690
2691
    /**
2692
     * Get the current array from the "Arrayy"-object.
2693
     *
2694
     * alias for "toArray()"
2695
     *
2696
     * @param bool $convertAllArrayyElements <p>
2697
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2698
     *                                       </p>
2699
     * @param bool $preserveKeys             <p>
2700
     *                                       e.g.: A generator maybe return the same key more then once,
2701
     *                                       so maybe you will ignore the keys.
2702
     *                                       </p>
2703
     *
2704
     * @return array
2705
     *
2706
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2707
     * @psalm-mutation-free
2708
     *
2709
     * @see Arrayy::toArray()
2710
     */
2711 500
    public function getArray(
2712
        bool $convertAllArrayyElements = false,
2713
        bool $preserveKeys = true
2714
    ): array {
2715 500
        return $this->toArray(
2716 500
            $convertAllArrayyElements,
2717 500
            $preserveKeys
2718
        );
2719
    }
2720
2721
    /**
2722
     * @param string $json
2723
     *
2724
     * @return $this
2725
     */
2726 3
    public static function createFromJsonMapper(string $json)
2727
    {
2728
        // init
2729 3
        $class = static::create();
2730
2731 3
        $jsonObject = \json_decode($json, false);
2732
2733 3
        $mapper = new \Arrayy\Mapper\Json();
2734 View Code Duplication
        $mapper->undefinedPropertyHandler = static function ($object, $key, $jsonValue) use ($class) {
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...
2735
            if ($class->checkPropertiesMismatchInConstructor) {
2736
                throw new \TypeError('Property mismatch - input: ' . \print_r(['key' => $key, 'jsonValue' => $jsonValue], true) . ' for object: ' . \get_class($object));
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'Property mismatch - inp...' . \get_class($object).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
2737
            }
2738
        };
2739
2740 3
        return $mapper->map($jsonObject, $class);
2741
    }
2742
2743
    /**
2744
     * @return array<int|string,TypeCheckInterface>|mixed|TypeCheckArray<int|string,TypeCheckInterface>|TypeInterface
0 ignored issues
show
Documentation introduced by
The doc-type array<int|string,TypeChe...nterface>|TypeInterface could not be parsed: Expected "|" or "end of type", but got "<" at position 57. (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...
2745
     *
2746
     * @internal
2747
     */
2748 6
    public function getPhpDocPropertiesFromClass()
2749
    {
2750 6
        if ($this->properties === []) {
2751 1
            $this->properties = $this->getPropertiesFromPhpDoc();
2752
        }
2753
2754 6
        return $this->properties;
2755
    }
2756
2757
    /**
2758
     * Get the current array from the "Arrayy"-object as list.
2759
     *
2760
     * alias for "toList()"
2761
     *
2762
     * @param bool $convertAllArrayyElements <p>
2763
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2764
     *                                       </p>
2765
     *
2766
     * @return array
2767
     *
2768
     * @psalm-return array<int,mixed>|array<int,T>
2769
     * @psalm-mutation-free
2770
     *
2771
     * @see Arrayy::toList()
2772
     */
2773 1
    public function getList(bool $convertAllArrayyElements = false): array
2774
    {
2775 1
        return $this->toList($convertAllArrayyElements);
2776
    }
2777
2778
    /**
2779
     * Returns the values from a single column of the input array, identified by
2780
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2781
     *
2782
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
2783
     * array by the values from the $indexKey column in the input array.
2784
     *
2785
     * @param mixed $columnKey
2786
     * @param mixed $indexKey
2787
     *
2788
     * @return static
2789
     *                <p>(Immutable)</p>
2790
     *
2791
     * @psalm-return static<TKey,T>
2792
     * @psalm-mutation-free
2793
     */
2794 1
    public function getColumn($columnKey = null, $indexKey = null): self
2795
    {
2796 1
        return static::create(
2797 1
            \array_column($this->toArray(), $columnKey, $indexKey),
2798 1
            $this->iteratorClass,
2799 1
            false
2800
        );
2801
    }
2802
2803
    /**
2804
     * Get the current array from the "Arrayy"-object as generator by reference.
2805
     *
2806
     * @return \Generator
2807
     *
2808
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2809
     */
2810 75
    public function &getGeneratorByReference(): \Generator
2811
    {
2812 75
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2813
            // -> false-positive -> see "&" from method
2814
            /** @noinspection YieldFromCanBeUsedInspection */
2815 17
            foreach ($this->generator as $key => $value) {
2816 17
                yield $key => $value;
2817
            }
2818
2819 5
            return;
2820
        }
2821
2822
        // -> false-positive -> see "&$value"
2823
        /** @noinspection YieldFromCanBeUsedInspection */
2824
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
2825 59
        foreach ($this->array as $key => &$value) {
2826 54
            yield $key => $value;
2827
        }
2828 35
    }
2829
2830
    /**
2831
     * Get the current array from the "Arrayy"-object as generator.
2832
     *
2833
     * @return \Generator
2834
     *
2835
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2836
     * @psalm-mutation-free
2837
     */
2838 996
    public function getGenerator(): \Generator
2839
    {
2840 996
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2841 25
            yield from $this->generator;
2842
2843 25
            return;
2844
        }
2845
2846 994
        yield from $this->array;
2847 968
    }
2848
2849
    /**
2850
     * alias: for "Arrayy->keys()"
2851
     *
2852
     * @return static
2853
     *                <p>(Immutable)</p>
2854
     *
2855
     * @see          Arrayy::keys()
2856
     *
2857
     * @psalm-return static<array-key,TKey>
2858
     * @psalm-mutation-free
2859
     */
2860 2
    public function getKeys()
2861
    {
2862 2
        return $this->keys();
2863
    }
2864
2865
    /**
2866
     * Get the current array from the "Arrayy"-object as object.
2867
     *
2868
     * @return \stdClass
2869
     */
2870 4
    public function getObject(): \stdClass
2871
    {
2872 4
        return self::arrayToObject($this->toArray());
2873
    }
2874
2875
    /**
2876
     * alias: for "Arrayy->randomImmutable()"
2877
     *
2878
     * @return static
2879
     *                <p>(Immutable)</p>
2880
     *
2881
     * @see          Arrayy::randomImmutable()
2882
     *
2883
     * @psalm-return static<int|array-key,T>
2884
     */
2885 4
    public function getRandom(): self
2886
    {
2887 4
        return $this->randomImmutable();
2888
    }
2889
2890
    /**
2891
     * alias: for "Arrayy->randomKey()"
2892
     *
2893
     * @return mixed
2894
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2895
     *
2896
     * @see Arrayy::randomKey()
2897
     */
2898 3
    public function getRandomKey()
2899
    {
2900 3
        return $this->randomKey();
2901
    }
2902
2903
    /**
2904
     * alias: for "Arrayy->randomKeys()"
2905
     *
2906
     * @param int $number
2907
     *
2908
     * @return static
2909
     *                <p>(Immutable)</p>
2910
     *
2911
     * @see          Arrayy::randomKeys()
2912
     *
2913
     * @psalm-return static<TKey,T>
2914
     */
2915 8
    public function getRandomKeys(int $number): self
2916
    {
2917 8
        return $this->randomKeys($number);
2918
    }
2919
2920
    /**
2921
     * alias: for "Arrayy->randomValue()"
2922
     *
2923
     * @return mixed
2924
     *               <p>Get a random value or null if there wasn't a value.</p>
2925
     *
2926
     * @see Arrayy::randomValue()
2927
     */
2928 3
    public function getRandomValue()
2929
    {
2930 3
        return $this->randomValue();
2931
    }
2932
2933
    /**
2934
     * alias: for "Arrayy->randomValues()"
2935
     *
2936
     * @param int $number
2937
     *
2938
     * @return static
2939
     *                <p>(Immutable)</p>
2940
     *
2941
     * @see          Arrayy::randomValues()
2942
     *
2943
     * @psalm-return static<TKey,T>
2944
     */
2945 6
    public function getRandomValues(int $number): self
2946
    {
2947 6
        return $this->randomValues($number);
2948
    }
2949
2950
    /**
2951
     * Gets all values.
2952
     *
2953
     * @return static
2954
     *                <p>The values of all elements in this array, in the order they
2955
     *                appear in the array.</p>
2956
     *
2957
     * @psalm-return static<TKey,T>
2958
     */
2959 4
    public function getValues()
2960
    {
2961 4
        $this->generatorToArray(false);
2962
2963 4
        return static::create(
2964 4
            \array_values($this->array),
2965 4
            $this->iteratorClass,
2966 4
            false
2967
        );
2968
    }
2969
2970
    /**
2971
     * Gets all values via Generator.
2972
     *
2973
     * @return \Generator
2974
     *                    <p>The values of all elements in this array, in the order they
2975
     *                    appear in the array as Generator.</p>
2976
     *
2977
     * @psalm-return \Generator<TKey,T>
2978
     */
2979 4
    public function getValuesYield(): \Generator
2980
    {
2981 4
        yield from $this->getGenerator();
2982 4
    }
2983
2984
    /**
2985
     * Group values from a array according to the results of a closure.
2986
     *
2987
     * @param callable|string $grouper  <p>A callable function name.</p>
2988
     * @param bool            $saveKeys
2989
     *
2990
     * @return static
2991
     *                <p>(Immutable)</p>
2992
     *
2993
     * @psalm-return static<TKey,T>
2994
     * @psalm-mutation-free
2995
     */
2996 4
    public function group($grouper, bool $saveKeys = false): self
2997
    {
2998
        // init
2999 4
        $result = [];
3000
3001
        // Iterate over values, group by property/results from closure.
3002 4
        foreach ($this->getGenerator() as $key => $value) {
3003 4
            if (\is_callable($grouper) === true) {
3004 3
                $groupKey = $grouper($value, $key);
3005
            } else {
3006 1
                $groupKey = $this->get($grouper);
3007
            }
3008
3009 4
            $newValue = $this->get($groupKey, null, $result);
3010
3011 4
            if ($groupKey instanceof self) {
3012
                $groupKey = $groupKey->toArray();
3013
            }
3014
3015 4
            if ($newValue instanceof self) {
3016 4
                $newValue = $newValue->toArray();
3017
            }
3018
3019
            // Add to results.
3020 4
            if ($groupKey !== null) {
3021 3
                if ($saveKeys) {
3022 2
                    $result[$groupKey] = $newValue;
3023 2
                    $result[$groupKey][$key] = $value;
3024
                } else {
3025 1
                    $result[$groupKey] = $newValue;
3026 1
                    $result[$groupKey][] = $value;
3027
                }
3028
            }
3029
        }
3030
3031 4
        return static::create(
3032 4
            $result,
3033 4
            $this->iteratorClass,
3034 4
            false
3035
        );
3036
    }
3037
3038
    /**
3039
     * Check if an array has a given key.
3040
     *
3041
     * @param mixed $key
3042
     *
3043
     * @return bool
3044
     */
3045 30
    public function has($key): bool
3046
    {
3047 30
        static $UN_FOUND = null;
3048
3049 30
        if ($UN_FOUND === null) {
3050
            // Generate unique string to use as marker.
3051 1
            $UN_FOUND = \uniqid('arrayy', true);
3052
        }
3053
3054 30
        if (\is_array($key)) {
3055 1
            if ($key === []) {
3056
                return false;
3057
            }
3058
3059 1
            foreach ($key as $keyTmp) {
3060 1
                $found = ($this->get($keyTmp, $UN_FOUND) !== $UN_FOUND);
3061 1
                if ($found === false) {
3062 1
                    return false;
3063
                }
3064
            }
3065
3066 1
            return true;
3067
        }
3068
3069 29
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
3070
    }
3071
3072
    /**
3073
     * Check if an array has a given value.
3074
     *
3075
     * INFO: if you need to search recursive please use ```contains()```
3076
     *
3077
     * @param mixed $value
3078
     *
3079
     * @return bool
3080
     */
3081 1
    public function hasValue($value): bool
3082
    {
3083 1
        return $this->contains($value);
3084
    }
3085
3086
    /**
3087
     * Implodes the values of this array.
3088
     *
3089
     * @param string $glue
3090
     *
3091
     * @return string
3092
     * @psalm-mutation-free
3093
     */
3094 28
    public function implode(string $glue = ''): string
3095
    {
3096 28
        return $this->implode_recursive($glue, $this->toArray(), false);
3097
    }
3098
3099
    /**
3100
     * Implodes the keys of this array.
3101
     *
3102
     * @param string $glue
3103
     *
3104
     * @return string
3105
     * @psalm-mutation-free
3106
     */
3107 8
    public function implodeKeys(string $glue = ''): string
3108
    {
3109 8
        return $this->implode_recursive($glue, $this->toArray(), true);
3110
    }
3111
3112
    /**
3113
     * Given a list and an iterate-function that returns
3114
     * a key for each element in the list (or a property name),
3115
     * returns an object with an index of each item.
3116
     *
3117
     * @param mixed $key
3118
     *
3119
     * @return static
3120
     *                <p>(Immutable)</p>
3121
     *
3122
     * @psalm-return static<TKey,T>
3123
     * @psalm-mutation-free
3124
     */
3125 4
    public function indexBy($key): self
3126
    {
3127
        // init
3128 4
        $results = [];
3129
3130 4
        foreach ($this->getGenerator() as $a) {
3131 4
            if (\array_key_exists($key, $a) === true) {
3132 3
                $results[$a[$key]] = $a;
3133
            }
3134
        }
3135
3136 4
        return static::create(
3137 4
            $results,
3138 4
            $this->iteratorClass,
3139 4
            false
3140
        );
3141
    }
3142
3143
    /**
3144
     * alias: for "Arrayy->searchIndex()"
3145
     *
3146
     * @param mixed $value <p>The value to search for.</p>
3147
     *
3148
     * @return false|mixed
3149
     *
3150
     * @see Arrayy::searchIndex()
3151
     */
3152 4
    public function indexOf($value)
3153
    {
3154 4
        return $this->searchIndex($value);
3155
    }
3156
3157
    /**
3158
     * Get everything but the last..$to items.
3159
     *
3160
     * @param int $to
3161
     *
3162
     * @return static
3163
     *                <p>(Immutable)</p>
3164
     *
3165
     * @psalm-return static<TKey,T>
3166
     * @psalm-mutation-free
3167
     */
3168 12
    public function initial(int $to = 1): self
3169
    {
3170 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
3171
    }
3172
3173
    /**
3174
     * Return an array with all elements found in input array.
3175
     *
3176
     * @param array $search
3177
     * @param bool  $keepKeys
3178
     *
3179
     * @return static
3180
     *                <p>(Immutable)</p>
3181
     *
3182
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
3183
     * @psalm-return static<TKey,T>
3184
     * @psalm-mutation-free
3185
     */
3186 4
    public function intersection(array $search, bool $keepKeys = false): self
3187
    {
3188 4
        if ($keepKeys) {
3189
            /**
3190
             * @psalm-suppress MissingClosureReturnType
3191
             * @psalm-suppress MissingClosureParamType
3192
             */
3193 1
            return static::create(
3194 1
                \array_uintersect(
3195 1
                    $this->toArray(),
3196 1
                    $search,
3197
                    static function ($a, $b) {
3198 1
                        return $a === $b ? 0 : -1;
3199 1
                    }
3200
                ),
3201 1
                $this->iteratorClass,
3202 1
                false
3203
            );
3204
        }
3205
3206 3
        return static::create(
3207 3
            \array_values(\array_intersect($this->toArray(), $search)),
3208 3
            $this->iteratorClass,
3209 3
            false
3210
        );
3211
    }
3212
3213
    /**
3214
     * Return an array with all elements found in input array.
3215
     *
3216
     * @param array ...$array
3217
     *
3218
     * @return static
3219
     *                <p>(Immutable)</p>
3220
     *
3221
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
3222
     * @psalm-return static<TKey,T>
3223
     * @psalm-mutation-free
3224
     */
3225 1
    public function intersectionMulti(...$array): self
3226
    {
3227 1
        return static::create(
3228 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
3229 1
            $this->iteratorClass,
3230 1
            false
3231
        );
3232
    }
3233
3234
    /**
3235
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
3236
     *
3237
     * @param array $search
3238
     *
3239
     * @return bool
3240
     *
3241
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
3242
     */
3243 1
    public function intersects(array $search): bool
3244
    {
3245 1
        return $this->intersection($search)->count() > 0;
3246
    }
3247
3248
    /**
3249
     * Invoke a function on all of an array's values.
3250
     *
3251
     * @param callable $callable
3252
     * @param mixed    $arguments
3253
     *
3254
     * @return static
3255
     *                <p>(Immutable)</p>
3256
     *
3257
     * @psalm-param  callable(T=,mixed):mixed $callable
3258
     * @psalm-return static<TKey,T>
3259
     * @psalm-mutation-free
3260
     */
3261 1 View Code Duplication
    public function invoke($callable, $arguments = []): 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...
3262
    {
3263
        // If one argument given for each iteration, create an array for it.
3264 1
        if (\is_array($arguments) === false) {
3265 1
            $arguments = \array_fill(
3266 1
                0,
3267 1
                $this->count(),
3268 1
                $arguments
3269
            );
3270
        }
3271
3272
        // If the callable has arguments, pass them.
3273 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...
3274 1
            $array = \array_map($callable, $this->toArray(), $arguments);
3275
        } else {
3276 1
            $array = $this->map($callable);
3277
        }
3278
3279 1
        return static::create(
3280 1
            $array,
3281 1
            $this->iteratorClass,
3282 1
            false
3283
        );
3284
    }
3285
3286
    /**
3287
     * Check whether array is associative or not.
3288
     *
3289
     * @param bool $recursive
3290
     *
3291
     * @return bool
3292
     *              <p>Returns true if associative, false otherwise.</p>
3293
     */
3294 15 View Code Duplication
    public function isAssoc(bool $recursive = false): 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...
3295
    {
3296 15
        if ($this->isEmpty()) {
3297 3
            return false;
3298
        }
3299
3300
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3301 13
        foreach ($this->keys($recursive)->getGeneratorByReference() as &$key) {
3302 13
            if ((string) $key !== $key) {
3303 11
                return false;
3304
            }
3305
        }
3306
3307 3
        return true;
3308
    }
3309
3310
    /**
3311
     * Check if a given key or keys are empty.
3312
     *
3313
     * @param int|int[]|string|string[]|null $keys
3314
     *
3315
     * @return bool
3316
     *              <p>Returns true if empty, false otherwise.</p>
3317
     * @psalm-mutation-free
3318
     */
3319 45
    public function isEmpty($keys = null): bool
3320
    {
3321 45
        if ($this->generator) {
3322
            return $this->toArray() === [];
3323
        }
3324
3325 45
        if ($keys === null) {
3326 43
            return $this->array === [];
3327
        }
3328
3329 2
        foreach ((array) $keys as $key) {
3330 2
            if (!empty($this->get($key))) {
3331 2
                return false;
3332
            }
3333
        }
3334
3335 2
        return true;
3336
    }
3337
3338
    /**
3339
     * Check if the current array is equal to the given "$array" or not.
3340
     *
3341
     * @param array $array
3342
     *
3343
     * @return bool
3344
     *
3345
     * @psalm-param array<mixed,mixed> $array
3346
     */
3347 1
    public function isEqual(array $array): bool
3348
    {
3349 1
        return $this->toArray() === $array;
3350
    }
3351
3352
    /**
3353
     * Check if the current array is a multi-array.
3354
     *
3355
     * @return bool
3356
     */
3357 22
    public function isMultiArray(): bool
3358
    {
3359
        return !(
3360 22
            \count($this->toArray(), \COUNT_NORMAL)
3361
            ===
3362 22
            \count($this->toArray(), \COUNT_RECURSIVE)
3363
        );
3364
    }
3365
3366
    /**
3367
     * Check whether array is numeric or not.
3368
     *
3369
     * @return bool
3370
     *              <p>Returns true if numeric, false otherwise.</p>
3371
     */
3372 5 View Code Duplication
    public function isNumeric(): 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...
3373
    {
3374 5
        if ($this->isEmpty()) {
3375 2
            return false;
3376
        }
3377
3378
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3379 4
        foreach ($this->keys()->getGeneratorByReference() as &$key) {
3380 4
            if ((int) $key !== $key) {
3381 2
                return false;
3382
            }
3383
        }
3384
3385 2
        return true;
3386
    }
3387
3388
    /**
3389
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3390
     *
3391
     * @param bool $recursive
3392
     *
3393
     * @return bool
3394
     * @psalm-mutation-free
3395
     */
3396 9
    public function isSequential(bool $recursive = false): bool
3397
    {
3398
3399
        // recursive
3400
3401 9
        if ($recursive === true) {
3402
            return $this->array_keys_recursive($this->toArray())
3403
                   ===
3404
                   \range(0, \count($this->toArray(), \COUNT_RECURSIVE) - 1);
3405
        }
3406
3407
        // non recursive
3408
3409 9
        return \array_keys($this->toArray())
3410
               ===
3411 9
               \range(0, \count($this->toArray(), \COUNT_NORMAL) - 1);
3412
    }
3413
3414
    /**
3415
     * @return array
3416
     *
3417
     * @psalm-return array<mixed,mixed>|array<TKey,T>
3418
     */
3419 2
    public function jsonSerialize(): array
3420
    {
3421 2
        return $this->toArray();
3422
    }
3423
3424
    /**
3425
     * Gets the key/index of the element at the current internal iterator position.
3426
     *
3427
     * @return int|string|null
3428
     */
3429
    public function key()
3430
    {
3431
        return \key($this->array);
3432
    }
3433
3434
    /**
3435
     * Checks if the given key exists in the provided array.
3436
     *
3437
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3438
     *       then you need to use "Arrayy->offsetExists()".
3439
     *
3440
     * @param int|string $key the key to look for
3441
     *
3442
     * @return bool
3443
     * @psalm-mutation-free
3444
     */
3445 162
    public function keyExists($key): bool
3446
    {
3447 162
        return \array_key_exists($key, $this->array);
3448
    }
3449
3450
    /**
3451
     * Get all keys from the current array.
3452
     *
3453
     * @param bool       $recursive     [optional] <p>
3454
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3455
     *                                  </p>
3456
     * @param mixed|null $search_values [optional] <p>
3457
     *                                  If specified, then only keys containing these values are returned.
3458
     *                                  </p>
3459
     * @param bool       $strict        [optional] <p>
3460
     *                                  Determines if strict comparison (===) should be used during the search.
3461
     *                                  </p>
3462
     *
3463
     * @return static
3464
     *                <p>(Immutable) An array of all the keys in input.</p>
3465
     *
3466
     * @psalm-return static<array-key,TKey>
3467
     * @psalm-mutation-free
3468
     */
3469 29
    public function keys(
3470
        bool $recursive = false,
3471
        $search_values = null,
3472
        bool $strict = true
3473
    ): self {
3474
3475
        // recursive
3476
3477 29
        if ($recursive === true) {
3478 4
            $array = $this->array_keys_recursive(
3479 4
                null,
3480 4
                $search_values,
3481 4
                $strict
3482
            );
3483
3484 4
            return static::create(
3485 4
                $array,
3486 4
                $this->iteratorClass,
3487 4
                false
3488
            );
3489
        }
3490
3491
        // non recursive
3492
3493 28
        if ($search_values === null) {
3494
            $arrayFunction = function (): \Generator {
3495 28
                foreach ($this->getGenerator() as $key => $value) {
3496 26
                    yield $key;
3497
                }
3498 28
            };
3499
        } else {
3500
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3501 1
                $is_array_tmp = \is_array($search_values);
3502
3503
                /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3504 1
                foreach ($this->getGeneratorByReference() as $key => &$value) {
3505 View Code Duplication
                    if (
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...
3506
                        (
3507 1
                            $is_array_tmp === false
3508
                            &&
3509 1
                            $strict === true
3510
                            &&
3511 1
                            $search_values === $value
3512
                        )
3513
                        ||
3514
                        (
3515 1
                            $is_array_tmp === false
3516
                            &&
3517 1
                            $strict === false
3518
                            &&
3519 1
                            $search_values == $value
3520
                        )
3521
                        ||
3522
                        (
3523 1
                            $is_array_tmp === true
3524
                            &&
3525 1
                            \in_array($value, $search_values, $strict)
3526
                        )
3527
                    ) {
3528 1
                        yield $key;
3529
                    }
3530
                }
3531 1
            };
3532
        }
3533
3534 28
        return static::create(
3535 28
            $arrayFunction,
3536 28
            $this->iteratorClass,
3537 28
            false
3538
        );
3539
    }
3540
3541
    /**
3542
     * Sort an array by key in reverse order.
3543
     *
3544
     * @param int $sort_flags [optional] <p>
3545
     *                        You may modify the behavior of the sort using the optional
3546
     *                        parameter sort_flags, for details
3547
     *                        see sort.
3548
     *                        </p>
3549
     *
3550
     * @return $this
3551
     *               <p>(Mutable) Return this Arrayy object.</p>
3552
     *
3553
     * @psalm-return static<TKey,T>
3554
     */
3555 4
    public function krsort(int $sort_flags = 0): self
3556
    {
3557 4
        $this->generatorToArray();
3558
3559 4
        \krsort($this->array, $sort_flags);
3560
3561 4
        return $this;
3562
    }
3563
3564
    /**
3565
     * Sort an array by key in reverse order.
3566
     *
3567
     * @param int $sort_flags [optional] <p>
3568
     *                        You may modify the behavior of the sort using the optional
3569
     *                        parameter sort_flags, for details
3570
     *                        see sort.
3571
     *                        </p>
3572
     *
3573
     * @return $this
3574
     *               <p>(Immutable) Return this Arrayy object.</p>
3575
     *
3576
     * @psalm-return static<TKey,T>
3577
     * @psalm-mutation-free
3578
     */
3579 4
    public function krsortImmutable(int $sort_flags = 0): self
3580
    {
3581 4
        $that = clone $this;
3582
3583
        /**
3584
         * @psalm-suppress ImpureMethodCall - object is already cloned
3585
         */
3586 4
        $that->krsort($sort_flags);
3587
3588 4
        return $that;
3589
    }
3590
3591
    /**
3592
     * Get the last value from the current array.
3593
     *
3594
     * @return mixed|null
3595
     *                    <p>Return null if there wasn't a element.</p>
3596
     * @psalm-mutation-free
3597
     */
3598 17
    public function last()
3599
    {
3600 17
        $key_last = $this->lastKey();
3601 17
        if ($key_last === null) {
3602 2
            return null;
3603
        }
3604
3605 15
        return $this->get($key_last);
3606
    }
3607
3608
    /**
3609
     * Get the last key from the current array.
3610
     *
3611
     * @return mixed|null
3612
     *                    <p>Return null if there wasn't a element.</p>
3613
     * @psalm-mutation-free
3614
     */
3615 21
    public function lastKey()
3616
    {
3617 21
        $this->generatorToArray();
3618
3619 21
        return \array_key_last($this->array);
3620
    }
3621
3622
    /**
3623
     * Get the last value(s) from the current array.
3624
     *
3625
     * @param int|null $number
3626
     *
3627
     * @return static
3628
     *                <p>(Immutable)</p>
3629
     *
3630
     * @psalm-return static<TKey,T>
3631
     * @psalm-mutation-free
3632
     */
3633 13
    public function lastsImmutable(int $number = null): self
3634
    {
3635 13
        if ($this->isEmpty()) {
3636 1
            return static::create(
3637 1
                [],
3638 1
                $this->iteratorClass,
3639 1
                false
3640
            );
3641
        }
3642
3643 12
        if ($number === null) {
3644 8
            $poppedValue = $this->last();
3645
3646 8
            if ($poppedValue === null) {
3647 1
                $poppedValue = [$poppedValue];
3648
            } else {
3649 7
                $poppedValue = (array) $poppedValue;
3650
            }
3651
3652 8
            $arrayy = static::create(
3653 8
                $poppedValue,
3654 8
                $this->iteratorClass,
3655 8
                false
3656
            );
3657
        } else {
3658 4
            $arrayy = $this->rest(-$number);
3659
        }
3660
3661 12
        return $arrayy;
3662
    }
3663
3664
    /**
3665
     * Get the last value(s) from the current array.
3666
     *
3667
     * @param int|null $number
3668
     *
3669
     * @return $this
3670
     *               <p>(Mutable)</p>
3671
     *
3672
     * @psalm-return static<TKey,T>
3673
     */
3674 13
    public function lastsMutable(int $number = null): self
3675
    {
3676 13
        if ($this->isEmpty()) {
3677 1
            return $this;
3678
        }
3679
3680 12
        if ($number === null) {
3681 8
            $poppedValue = $this->last();
3682
3683 8
            if ($poppedValue === null) {
3684 1
                $poppedValue = [$poppedValue];
3685
            } else {
3686 7
                $poppedValue = (array) $poppedValue;
3687
            }
3688
3689 8
            $this->array = static::create(
3690 8
                $poppedValue,
3691 8
                $this->iteratorClass,
3692 8
                false
3693 8
            )->toArray();
3694
        } else {
3695 4
            $this->array = $this->rest(-$number)->toArray();
3696
        }
3697
3698 12
        $this->generator = null;
3699
3700 12
        return $this;
3701
    }
3702
3703
    /**
3704
     * Count the values from the current array.
3705
     *
3706
     * alias: for "Arrayy->count()"
3707
     *
3708
     * @param int $mode
3709
     *
3710
     * @return int
3711
     *
3712
     * @see Arrayy::count()
3713
     */
3714 20
    public function length(int $mode = \COUNT_NORMAL): int
3715
    {
3716 20
        return $this->count($mode);
3717
    }
3718
3719
    /**
3720
     * Apply the given function to the every element of the array,
3721
     * collecting the results.
3722
     *
3723
     * @param callable $callable
3724
     * @param bool     $useKeyAsSecondParameter
3725
     * @param mixed    ...$arguments
3726
     *
3727
     * @return static
3728
     *                <p>(Immutable) Arrayy object with modified elements.</p>
3729
     *
3730
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
3731
     * @psalm-return static<TKey,T>
3732
     * @psalm-mutation-free
3733
     */
3734 5
    public function map(
3735
        callable $callable,
3736
        bool $useKeyAsSecondParameter = false,
3737
        ...$arguments
3738
    ) {
3739
        /**
3740
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
3741
         */
3742 5
        $useArguments = \func_num_args() > 2;
3743
3744 5
        return static::create(
3745
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
3746 5
                foreach ($this->getGenerator() as $key => $value) {
3747 4
                    if ($useArguments) {
3748 3
                        if ($useKeyAsSecondParameter) {
3749
                            yield $key => $callable($value, $key, ...$arguments);
3750
                        } else {
3751 3
                            yield $key => $callable($value, ...$arguments);
3752
                        }
3753
                    } else {
3754
                        /** @noinspection NestedPositiveIfStatementsInspection */
3755 4
                        if ($useKeyAsSecondParameter) {
3756
                            yield $key => $callable($value, $key);
3757
                        } else {
3758 4
                            yield $key => $callable($value);
3759
                        }
3760
                    }
3761
                }
3762 5
            },
3763 5
            $this->iteratorClass,
3764 5
            false
3765
        );
3766
    }
3767
3768
    /**
3769
     * Check if all items in current array match a truth test.
3770
     *
3771
     * @param \Closure $closure
3772
     *
3773
     * @return bool
3774
     */
3775 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...
3776
    {
3777 15
        if ($this->count() === 0) {
3778 2
            return false;
3779
        }
3780
3781 13
        foreach ($this->getGenerator() as $key => $value) {
3782 13
            $value = $closure($value, $key);
3783
3784 13
            if ($value === false) {
3785 7
                return false;
3786
            }
3787
        }
3788
3789 7
        return true;
3790
    }
3791
3792
    /**
3793
     * Check if any item in the current array matches a truth test.
3794
     *
3795
     * @param \Closure $closure
3796
     *
3797
     * @return bool
3798
     */
3799 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...
3800
    {
3801 14
        if ($this->count() === 0) {
3802 2
            return false;
3803
        }
3804
3805 12
        foreach ($this->getGenerator() as $key => $value) {
3806 12
            $value = $closure($value, $key);
3807
3808 12
            if ($value === true) {
3809 9
                return true;
3810
            }
3811
        }
3812
3813 4
        return false;
3814
    }
3815
3816
    /**
3817
     * Get the max value from an array.
3818
     *
3819
     * @return false|mixed
3820
     *                     <p>Will return false if there are no values.</p>
3821
     */
3822 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...
3823
    {
3824 10
        if ($this->count() === 0) {
3825 1
            return false;
3826
        }
3827
3828 9
        $max = false;
3829
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3830 9
        foreach ($this->getGeneratorByReference() as &$value) {
3831
            if (
3832 9
                $max === false
3833
                ||
3834 9
                $value > $max
3835
            ) {
3836 9
                $max = $value;
3837
            }
3838
        }
3839
3840 9
        return $max;
3841
    }
3842
3843
    /**
3844
     * Merge the new $array into the current array.
3845
     *
3846
     * - keep key,value from the current array, also if the index is in the new $array
3847
     *
3848
     * @param array $array
3849
     * @param bool  $recursive
3850
     *
3851
     * @return static
3852
     *                <p>(Immutable)</p>
3853
     *
3854
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3855
     * @psalm-return static<int|TKey,T>
3856
     * @psalm-mutation-free
3857
     */
3858 32 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...
3859
    {
3860 32
        if ($recursive === true) {
3861 9
            $array = $this->getArrayRecursiveHelperArrayy($array);
3862 9
            $result = \array_replace_recursive($this->toArray(), $array);
3863
        } else {
3864 23
            $result = \array_replace($this->toArray(), $array);
3865
        }
3866
3867 32
        return static::create(
3868 32
            $result,
3869 32
            $this->iteratorClass,
3870 32
            false
3871
        );
3872
    }
3873
3874
    /**
3875
     * Merge the new $array into the current array.
3876
     *
3877
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
3878
     * - create new indexes
3879
     *
3880
     * @param array $array
3881
     * @param bool  $recursive
3882
     *
3883
     * @return static
3884
     *                <p>(Immutable)</p>
3885
     *
3886
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3887
     * @psalm-return static<TKey,T>
3888
     * @psalm-mutation-free
3889
     */
3890 19 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...
3891
    {
3892 19
        if ($recursive === true) {
3893 5
            $array = $this->getArrayRecursiveHelperArrayy($array);
3894 5
            $result = \array_merge_recursive($this->toArray(), $array);
3895
        } else {
3896 14
            $result = \array_merge($this->toArray(), $array);
3897
        }
3898
3899 19
        return static::create(
3900 19
            $result,
3901 19
            $this->iteratorClass,
3902 19
            false
3903
        );
3904
    }
3905
3906
    /**
3907
     * Merge the the current array into the $array.
3908
     *
3909
     * - use key,value from the new $array, also if the index is in the current array
3910
     *
3911
     * @param array $array
3912
     * @param bool  $recursive
3913
     *
3914
     * @return static
3915
     *                <p>(Immutable)</p>
3916
     *
3917
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3918
     * @psalm-return static<TKey,T>
3919
     * @psalm-mutation-free
3920
     */
3921 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...
3922
    {
3923 16
        if ($recursive === true) {
3924 4
            $array = $this->getArrayRecursiveHelperArrayy($array);
3925 4
            $result = \array_replace_recursive($array, $this->toArray());
3926
        } else {
3927 12
            $result = \array_replace($array, $this->toArray());
3928
        }
3929
3930 16
        return static::create(
3931 16
            $result,
3932 16
            $this->iteratorClass,
3933 16
            false
3934
        );
3935
    }
3936
3937
    /**
3938
     * Merge the current array into the new $array.
3939
     *
3940
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
3941
     * - create new indexes
3942
     *
3943
     * @param array $array
3944
     * @param bool  $recursive
3945
     *
3946
     * @return static
3947
     *                <p>(Immutable)</p>
3948
     *
3949
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3950
     * @psalm-return static<TKey,T>
3951
     * @psalm-mutation-free
3952
     */
3953 20 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...
3954
    {
3955 20
        if ($recursive === true) {
3956 7
            $array = $this->getArrayRecursiveHelperArrayy($array);
3957 7
            $result = \array_merge_recursive($array, $this->toArray());
3958
        } else {
3959 13
            $result = \array_merge($array, $this->toArray());
3960
        }
3961
3962 20
        return static::create(
3963 20
            $result,
3964 20
            $this->iteratorClass,
3965 20
            false
3966
        );
3967
    }
3968
3969
    /**
3970
     * @return ArrayyMeta|static
3971
     */
3972 16
    public static function meta()
3973
    {
3974 16
        return (new ArrayyMeta())->getMetaObject(static::class);
3975
    }
3976
3977
    /**
3978
     * Get the min value from an array.
3979
     *
3980
     * @return false|mixed
3981
     *                     <p>Will return false if there are no values.</p>
3982
     */
3983 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...
3984
    {
3985 10
        if ($this->count() === 0) {
3986 1
            return false;
3987
        }
3988
3989 9
        $min = false;
3990
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3991 9
        foreach ($this->getGeneratorByReference() as &$value) {
3992
            if (
3993 9
                $min === false
3994
                ||
3995 9
                $value < $min
3996
            ) {
3997 9
                $min = $value;
3998
            }
3999
        }
4000
4001 9
        return $min;
4002
    }
4003
4004
    /**
4005
     * Get the most used value from the array.
4006
     *
4007
     * @return mixed|null
4008
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
4009
     * @psalm-mutation-free
4010
     */
4011 3
    public function mostUsedValue()
4012
    {
4013 3
        return $this->countValues()->arsortImmutable()->firstKey();
4014
    }
4015
4016
    /**
4017
     * Get the most used value from the array.
4018
     *
4019
     * @param int|null $number <p>How many values you will take?</p>
4020
     *
4021
     * @return static
4022
     *                <p>(Immutable)</p>
4023
     *
4024
     * @psalm-return static<TKey,T>
4025
     * @psalm-mutation-free
4026
     */
4027 3
    public function mostUsedValues(int $number = null): self
4028
    {
4029 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
4030
    }
4031
4032
    /**
4033
     * Move an array element to a new index.
4034
     *
4035
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
4036
     *
4037
     * @param int|string $from
4038
     * @param int        $to
4039
     *
4040
     * @return static
4041
     *                <p>(Immutable)</p>
4042
     *
4043
     * @psalm-return static<TKey,T>
4044
     * @psalm-mutation-free
4045
     */
4046 1
    public function moveElement($from, $to): self
4047
    {
4048 1
        $array = $this->toArray();
4049
4050 1
        if ((int) $from === $from) {
4051 1
            $tmp = \array_splice($array, $from, 1);
4052 1
            \array_splice($array, (int) $to, 0, $tmp);
4053 1
            $output = $array;
4054 1
        } elseif ((string) $from === $from) {
4055 1
            $indexToMove = \array_search($from, \array_keys($array), true);
4056 1
            $itemToMove = $array[$from];
4057 1
            if ($indexToMove !== false) {
4058 1
                \array_splice($array, $indexToMove, 1);
4059
            }
4060 1
            $i = 0;
4061 1
            $output = [];
4062 1
            foreach ($array as $key => $item) {
4063 1
                if ($i === $to) {
4064 1
                    $output[$from] = $itemToMove;
4065
                }
4066 1
                $output[$key] = $item;
4067 1
                ++$i;
4068
            }
4069
        } else {
4070
            $output = [];
4071
        }
4072
4073 1
        return static::create(
4074 1
            $output,
4075 1
            $this->iteratorClass,
4076 1
            false
4077
        );
4078
    }
4079
4080
    /**
4081
     * Move an array element to the first place.
4082
     *
4083
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4084
     *       loss the keys of an indexed array.
4085
     *
4086
     * @param int|string $key
4087
     *
4088
     * @return static
4089
     *                <p>(Immutable)</p>
4090
     *
4091
     * @psalm-return static<TKey,T>
4092
     * @psalm-mutation-free
4093
     */
4094 1 View Code Duplication
    public function moveElementToFirstPlace($key): 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...
4095
    {
4096 1
        $array = $this->toArray();
4097
4098 1
        if ($this->offsetExists($key)) {
4099 1
            $tmpValue = $this->get($key);
4100 1
            unset($array[$key]);
4101 1
            $array = [$key => $tmpValue] + $array;
4102
        }
4103
4104 1
        return static::create(
4105 1
            $array,
4106 1
            $this->iteratorClass,
4107 1
            false
4108
        );
4109
    }
4110
4111
    /**
4112
     * Move an array element to the last place.
4113
     *
4114
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4115
     *       loss the keys of an indexed array.
4116
     *
4117
     * @param int|string $key
4118
     *
4119
     * @return static
4120
     *                <p>(Immutable)</p>
4121
     *
4122
     * @psalm-return static<TKey,T>
4123
     * @psalm-mutation-free
4124
     */
4125 1 View Code Duplication
    public function moveElementToLastPlace($key): 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...
4126
    {
4127 1
        $array = $this->toArray();
4128
4129 1
        if ($this->offsetExists($key)) {
4130 1
            $tmpValue = $this->get($key);
4131 1
            unset($array[$key]);
4132 1
            $array += [$key => $tmpValue];
4133
        }
4134
4135 1
        return static::create(
4136 1
            $array,
4137 1
            $this->iteratorClass,
4138 1
            false
4139
        );
4140
    }
4141
4142
    /**
4143
     * Moves the internal iterator position to the next element and returns this element.
4144
     *
4145
     * @return false|mixed
4146
     *                     <p>(Mutable) Will return false if there are no values.</p>
4147
     */
4148
    public function next()
4149
    {
4150
        return \next($this->array);
4151
    }
4152
4153
    /**
4154
     * Get the next nth keys and values from the array.
4155
     *
4156
     * @param int $step
4157
     * @param int $offset
4158
     *
4159
     * @return static
4160
     *                <p>(Immutable)</p>
4161
     *
4162
     * @psalm-return static<TKey,T>
4163
     * @psalm-mutation-free
4164
     */
4165 1
    public function nth(int $step, int $offset = 0): self
4166
    {
4167
        $arrayFunction = function () use ($step, $offset): \Generator {
4168 1
            $position = 0;
4169 1
            foreach ($this->getGenerator() as $key => $value) {
4170 1
                if ($position++ % $step !== $offset) {
4171 1
                    continue;
4172
                }
4173
4174 1
                yield $key => $value;
4175
            }
4176 1
        };
4177
4178 1
        return static::create(
4179 1
            $arrayFunction,
4180 1
            $this->iteratorClass,
4181 1
            false
4182
        );
4183
    }
4184
4185
    /**
4186
     * Get a subset of the items from the given array.
4187
     *
4188
     * @param mixed[] $keys
4189
     *
4190
     * @return static
4191
     *                <p>(Immutable)</p>
4192
     *
4193
     * @psalm-return static<TKey,T>
4194
     * @psalm-mutation-free
4195
     */
4196 1
    public function only(array $keys): self
4197
    {
4198 1
        $array = $this->toArray();
4199
4200 1
        return static::create(
4201 1
            \array_intersect_key($array, \array_flip($keys)),
4202 1
            $this->iteratorClass,
4203 1
            false
4204
        );
4205
    }
4206
4207
    /**
4208
     * Pad array to the specified size with a given value.
4209
     *
4210
     * @param int   $size  <p>Size of the result array.</p>
4211
     * @param mixed $value <p>Empty value by default.</p>
4212
     *
4213
     * @return static
4214
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
4215
     *
4216
     * @psalm-return static<TKey,T>
4217
     * @psalm-mutation-free
4218
     */
4219 5
    public function pad(int $size, $value): self
4220
    {
4221 5
        return static::create(
4222 5
            \array_pad($this->toArray(), $size, $value),
4223 5
            $this->iteratorClass,
4224 5
            false
4225
        );
4226
    }
4227
4228
    /**
4229
     * Partitions this array in two array according to a predicate.
4230
     * Keys are preserved in the resulting array.
4231
     *
4232
     * @param \Closure $closure
4233
     *                          <p>The predicate on which to partition.</p>
4234
     *
4235
     * @return array<int, static>
0 ignored issues
show
Documentation introduced by
The doc-type array<int, could not be parsed: Expected ">" at position 5, but found "end of type". (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...
4236
     *                    <p>An array with two elements. The first element contains the array
4237
     *                    of elements where the predicate returned TRUE, the second element
4238
     *                    contains the array of elements where the predicate returned FALSE.</p>
4239
     *
4240
     * @psalm-return array<int, static<TKey,T>>
4241
     */
4242 1
    public function partition(\Closure $closure): array
4243
    {
4244
        // init
4245 1
        $matches = [];
4246 1
        $noMatches = [];
4247
4248 1
        foreach ($this->array as $key => $value) {
4249 1
            if ($closure($value, $key)) {
4250 1
                $matches[$key] = $value;
4251
            } else {
4252 1
                $noMatches[$key] = $value;
4253
            }
4254
        }
4255
4256 1
        return [self::create($matches), self::create($noMatches)];
4257
    }
4258
4259
    /**
4260
     * Pop a specified value off the end of the current array.
4261
     *
4262
     * @return mixed|null
4263
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
4264
     */
4265 5
    public function pop()
4266
    {
4267 5
        $this->generatorToArray();
4268
4269 5
        return \array_pop($this->array);
4270
    }
4271
4272
    /**
4273
     * Prepend a (key) + value to the current array.
4274
     *
4275
     * EXAMPLE: <code>
4276
     * a(['fòô' => 'bàř'])->prepend('foo'); // Arrayy[0 => 'foo', 'fòô' => 'bàř']
4277
     * </code>
4278
     *
4279
     * @param mixed $value
4280
     * @param mixed $key
4281
     *
4282
     * @return $this
4283
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
4284
     *
4285
     * @psalm-return static<TKey,T>
4286
     */
4287 11
    public function prepend($value, $key = null)
4288
    {
4289 11
        $this->generatorToArray();
4290
4291 11
        if ($this->properties !== []) {
4292 3
            $this->checkType($key, $value);
4293
        }
4294
4295 9
        if ($key === null) {
4296 8
            \array_unshift($this->array, $value);
4297
        } else {
4298 2
            $this->array = [$key => $value] + $this->array;
4299
        }
4300
4301 9
        return $this;
4302
    }
4303
4304
    /**
4305
     * Add a suffix to each key.
4306
     *
4307
     * @param mixed $suffix
4308
     *
4309
     * @return static
4310
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
4311
     *
4312
     * @psalm-return static<TKey,T>
4313
     * @psalm-mutation-free
4314
     */
4315 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...
4316
    {
4317
        // init
4318 10
        $result = [];
4319
4320 10
        foreach ($this->getGenerator() as $key => $item) {
4321 9
            if ($item instanceof self) {
4322
                $result[$key] = $item->prependToEachKey($suffix);
4323 9
            } elseif (\is_array($item) === true) {
4324
                $result[$key] = self::create(
4325
                    $item,
4326
                    $this->iteratorClass,
4327
                    false
4328
                )->prependToEachKey($suffix)
4329
                    ->toArray();
4330
            } else {
4331 9
                $result[$key . $suffix] = $item;
4332
            }
4333
        }
4334
4335 10
        return self::create(
4336 10
            $result,
4337 10
            $this->iteratorClass,
4338 10
            false
4339
        );
4340
    }
4341
4342
    /**
4343
     * Add a suffix to each value.
4344
     *
4345
     * @param mixed $suffix
4346
     *
4347
     * @return static
4348
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
4349
     *
4350
     * @psalm-return static<TKey,T>
4351
     * @psalm-mutation-free
4352
     */
4353 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...
4354
    {
4355
        // init
4356 10
        $result = [];
4357
4358 10
        foreach ($this->getGenerator() as $key => $item) {
4359 9
            if ($item instanceof self) {
4360
                $result[$key] = $item->prependToEachValue($suffix);
4361 9
            } elseif (\is_array($item) === true) {
4362
                $result[$key] = self::create(
4363
                    $item,
4364
                    $this->iteratorClass,
4365
                    false
4366
                )->prependToEachValue($suffix)
4367
                    ->toArray();
4368 9
            } elseif (\is_object($item) === true) {
4369 1
                $result[$key] = $item;
4370
            } else {
4371 8
                $result[$key] = $item . $suffix;
4372
            }
4373
        }
4374
4375 10
        return self::create(
4376 10
            $result,
4377 10
            $this->iteratorClass,
4378 10
            false
4379
        );
4380
    }
4381
4382
    /**
4383
     * Return the value of a given key and
4384
     * delete the key.
4385
     *
4386
     * @param int|int[]|string|string[]|null $keyOrKeys
4387
     * @param mixed                          $fallback
4388
     *
4389
     * @return mixed
4390
     */
4391 5
    public function pull($keyOrKeys = null, $fallback = null)
4392
    {
4393 5
        if ($keyOrKeys === null) {
4394 1
            $array = $this->toArray();
4395 1
            $this->clear();
4396
4397 1
            return $array;
4398
        }
4399
4400 4
        if (\is_array($keyOrKeys) === true) {
4401 1
            $valueOrValues = [];
4402 1
            foreach ($keyOrKeys as $key) {
4403 1
                $valueOrValues[] = $this->get($key, $fallback);
4404 1
                $this->offsetUnset($key);
4405
            }
4406
        } else {
4407 4
            $valueOrValues = $this->get($keyOrKeys, $fallback);
4408 4
            $this->offsetUnset($keyOrKeys);
4409
        }
4410
4411 4
        return $valueOrValues;
4412
    }
4413
4414
    /**
4415
     * Push one or more values onto the end of array at once.
4416
     *
4417
     * @param array ...$args
4418
     *
4419
     * @return $this
4420
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
4421
     *
4422
     * @noinspection ReturnTypeCanBeDeclaredInspection
4423
     *
4424
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
4425
     * @psalm-return static<TKey,T>
4426
     */
4427 7
    public function push(...$args)
4428
    {
4429 7
        $this->generatorToArray();
4430
4431
        if (
4432 7
            $this->checkPropertyTypes
4433
            &&
4434 7
            $this->properties !== []
4435
        ) {
4436 1
            foreach ($args as $key => $value) {
4437 1
                $this->checkType($key, $value);
4438
            }
4439
        }
4440
4441 7
        \array_push($this->array, ...$args);
4442
4443 7
        return $this;
4444
    }
4445
4446
    /**
4447
     * Get a random value from the current array.
4448
     *
4449
     * @param int|null $number <p>How many values you will take?</p>
4450
     *
4451
     * @return static
4452
     *                <p>(Immutable)</p>
4453
     *
4454
     * @psalm-return static<int|array-key,T>
4455
     */
4456 19
    public function randomImmutable(int $number = null): self
4457
    {
4458 19
        $this->generatorToArray();
4459
4460 19
        if ($this->count() === 0) {
4461 1
            return static::create(
4462 1
                [],
4463 1
                $this->iteratorClass,
4464 1
                false
4465
            );
4466
        }
4467
4468 18 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...
4469
            /** @noinspection NonSecureArrayRandUsageInspection */
4470 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4471
4472 13
            return static::create(
4473 13
                $arrayRandValue,
4474 13
                $this->iteratorClass,
4475 13
                false
4476
            );
4477
        }
4478
4479 6
        $arrayTmp = $this->array;
4480
        /** @noinspection NonSecureShuffleUsageInspection */
4481 6
        \shuffle($arrayTmp);
4482
4483 6
        return static::create(
4484 6
            $arrayTmp,
4485 6
            $this->iteratorClass,
4486 6
            false
4487 6
        )->firstsImmutable($number);
4488
    }
4489
4490
    /**
4491
     * Pick a random key/index from the keys of this array.
4492
     *
4493
     * @throws \RangeException If array is empty
4494
     *
4495
     * @return mixed
4496
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
4497
     */
4498 4
    public function randomKey()
4499
    {
4500 4
        $result = $this->randomKeys(1);
4501
4502 4
        if (!isset($result[0])) {
4503
            $result[0] = null;
4504
        }
4505
4506 4
        return $result[0];
4507
    }
4508
4509
    /**
4510
     * Pick a given number of random keys/indexes out of this array.
4511
     *
4512
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
4513
     *
4514
     * @throws \RangeException If array is empty
4515
     *
4516
     * @return static
4517
     *                <p>(Immutable)</p>
4518
     *
4519
     * @psalm-return static<TKey,T>
4520
     */
4521 13
    public function randomKeys(int $number): self
4522
    {
4523 13
        $this->generatorToArray();
4524
4525 13
        $count = $this->count();
4526
4527
        if (
4528 13
            $number === 0
4529
            ||
4530 13
            $number > $count
4531
        ) {
4532 2
            throw new \RangeException(
4533 2
                \sprintf(
4534 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
4535 2
                    $number,
4536 2
                    $count
4537
                )
4538
            );
4539
        }
4540
4541 11
        $result = (array) \array_rand($this->array, $number);
4542
4543 11
        return static::create(
4544 11
            $result,
4545 11
            $this->iteratorClass,
4546 11
            false
4547
        );
4548
    }
4549
4550
    /**
4551
     * Get a random value from the current array.
4552
     *
4553
     * @param int|null $number <p>How many values you will take?</p>
4554
     *
4555
     * @return $this
4556
     *               <p>(Mutable) Return this Arrayy object.</p>
4557
     *
4558
     * @psalm-return static<TKey,T>
4559
     */
4560 17
    public function randomMutable(int $number = null): self
4561
    {
4562 17
        $this->generatorToArray();
4563
4564 17
        if ($this->count() === 0) {
4565
            return static::create(
4566
                [],
4567
                $this->iteratorClass,
4568
                false
4569
            );
4570
        }
4571
4572 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...
4573
            /** @noinspection NonSecureArrayRandUsageInspection */
4574 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4575 7
            $this->array = $arrayRandValue;
4576
4577 7
            return $this;
4578
        }
4579
4580
        /** @noinspection NonSecureShuffleUsageInspection */
4581 11
        \shuffle($this->array);
4582
4583 11
        return $this->firstsMutable($number);
4584
    }
4585
4586
    /**
4587
     * Pick a random value from the values of this array.
4588
     *
4589
     * @return mixed
4590
     *               <p>Get a random value or null if there wasn't a value.</p>
4591
     */
4592 4
    public function randomValue()
4593
    {
4594 4
        $result = $this->randomImmutable();
4595
4596 4
        if (!isset($result[0])) {
4597
            $result[0] = null;
4598
        }
4599
4600 4
        return $result[0];
4601
    }
4602
4603
    /**
4604
     * Pick a given number of random values out of this array.
4605
     *
4606
     * @param int $number
4607
     *
4608
     * @return static
4609
     *                <p>(Mutable)</p>
4610
     *
4611
     * @psalm-return static<TKey,T>
4612
     */
4613 7
    public function randomValues(int $number): self
4614
    {
4615 7
        return $this->randomMutable($number);
4616
    }
4617
4618
    /**
4619
     * Get a random value from an array, with the ability to skew the results.
4620
     *
4621
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
4622
     *
4623
     * @param array    $array
4624
     * @param int|null $number <p>How many values you will take?</p>
4625
     *
4626
     * @return static<int,mixed>
0 ignored issues
show
Documentation introduced by
The doc-type static<int,mixed> could not be parsed: Expected "|" or "end of type", but got "<" at position 6. (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...
4627
     *                           <p>(Immutable)</p>
4628
     *
4629
     * @psalm-param  array<mixed,mixed> $array
4630
     * @psalm-return static<int|array-key,T>
4631
     */
4632 9
    public function randomWeighted(array $array, int $number = null): self
4633
    {
4634
        // init
4635 9
        $options = [];
4636
4637 9
        foreach ($array as $option => $weight) {
4638 9
            if ($this->searchIndex($option) !== false) {
4639 2
                for ($i = 0; $i < $weight; ++$i) {
4640 1
                    $options[] = $option;
4641
                }
4642
            }
4643
        }
4644
4645 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
4646
    }
4647
4648
    /**
4649
     * Reduce the current array via callable e.g. anonymous-function.
4650
     *
4651
     * @param callable $callable
4652
     * @param mixed    $init
4653
     *
4654
     * @return static
4655
     *                <p>(Immutable)</p>
4656
     *
4657
     * @psalm-return static<TKey,T>
4658
     * @psalm-mutation-free
4659
     */
4660 18
    public function reduce($callable, $init = []): self
4661
    {
4662 18
        if ($this->generator) {
4663 1
            $result = $init;
4664
4665 1
            foreach ($this->getGenerator() as $value) {
4666 1
                $result = $callable($result, $value);
4667
            }
4668
4669 1
            return static::create(
4670 1
                $result,
4671 1
                $this->iteratorClass,
4672 1
                false
4673
            );
4674
        }
4675
4676 18
        $result = \array_reduce($this->array, $callable, $init);
4677
4678 18
        if ($result === null) {
4679
            $this->array = [];
4680
        } else {
4681 18
            $this->array = (array) $result;
4682
        }
4683
4684 18
        return static::create(
4685 18
            $this->array,
4686 18
            $this->iteratorClass,
4687 18
            false
4688
        );
4689
    }
4690
4691
    /**
4692
     * @param bool $unique
4693
     *
4694
     * @return static
4695
     *                <p>(Immutable)</p>
4696
     *
4697
     * @psalm-return static<TKey,T>
4698
     * @psalm-mutation-free
4699
     */
4700 14
    public function reduce_dimension(bool $unique = true): self
4701
    {
4702
        // init
4703 14
        $result = [];
4704
4705 14
        foreach ($this->getGenerator() as $val) {
4706 12
            if (\is_array($val) === true) {
4707 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
4708
            } else {
4709 12
                $result[] = [$val];
4710
            }
4711
        }
4712
4713 14
        $result = $result === [] ? [] : \array_merge(...$result);
4714
4715 14
        $resultArrayy = new static($result);
4716
4717
        /**
4718
         * @psalm-suppress ImpureMethodCall - object is already re-created
4719
         * @psalm-suppress InvalidReturnStatement - why?
4720
         */
4721 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
4722
    }
4723
4724
    /**
4725
     * Create a numerically re-indexed Arrayy object.
4726
     *
4727
     * @return $this
4728
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
4729
     *
4730
     * @psalm-return static<TKey,T>
4731
     */
4732 9
    public function reindex(): self
4733
    {
4734 9
        $this->generatorToArray(false);
4735
4736 9
        $this->array = \array_values($this->array);
4737
4738 9
        return $this;
4739
    }
4740
4741
    /**
4742
     * Return all items that fail the truth test.
4743
     *
4744
     * @param \Closure $closure
4745
     *
4746
     * @return static
4747
     *                <p>(Immutable)</p>
4748
     *
4749
     * @psalm-return static<TKey,T>
4750
     * @psalm-mutation-free
4751
     */
4752 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...
4753
    {
4754
        // init
4755 1
        $filtered = [];
4756
4757 1
        foreach ($this->getGenerator() as $key => $value) {
4758 1
            if (!$closure($value, $key)) {
4759 1
                $filtered[$key] = $value;
4760
            }
4761
        }
4762
4763 1
        return static::create(
4764 1
            $filtered,
4765 1
            $this->iteratorClass,
4766 1
            false
4767
        );
4768
    }
4769
4770
    /**
4771
     * Remove a value from the current array (optional using dot-notation).
4772
     *
4773
     * @param mixed $key
4774
     *
4775
     * @return static
4776
     *                <p>(Mutable)</p>
4777
     *
4778
     * @psalm-param  TKey $key
4779
     * @psalm-return static<TKey,T>
4780
     */
4781 21
    public function remove($key)
4782
    {
4783
        // recursive call
4784 21
        if (\is_array($key) === true) {
4785
            foreach ($key as $k) {
4786
                $this->internalRemove($k);
4787
            }
4788
4789
            return static::create(
4790
                $this->toArray(),
4791
                $this->iteratorClass,
4792
                false
4793
            );
4794
        }
4795
4796 21
        $this->internalRemove($key);
4797
4798 21
        return static::create(
4799 21
            $this->toArray(),
4800 21
            $this->iteratorClass,
4801 21
            false
4802
        );
4803
    }
4804
4805
    /**
4806
     * alias: for "Arrayy->removeValue()"
4807
     *
4808
     * @param mixed $element
4809
     *
4810
     * @return static
4811
     *                <p>(Immutable)</p>
4812
     *
4813
     * @psalm-param  T $element
4814
     * @psalm-return static<TKey,T>
4815
     * @psalm-mutation-free
4816
     */
4817 8
    public function removeElement($element)
4818
    {
4819 8
        return $this->removeValue($element);
4820
    }
4821
4822
    /**
4823
     * Remove the first value from the current array.
4824
     *
4825
     * @return static
4826
     *                <p>(Immutable)</p>
4827
     *
4828
     * @psalm-return static<TKey,T>
4829
     * @psalm-mutation-free
4830
     */
4831 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...
4832
    {
4833 7
        $tmpArray = $this->toArray();
4834
4835 7
        \array_shift($tmpArray);
4836
4837 7
        return static::create(
4838 7
            $tmpArray,
4839 7
            $this->iteratorClass,
4840 7
            false
4841
        );
4842
    }
4843
4844
    /**
4845
     * Remove the last value from the current array.
4846
     *
4847
     * @return static
4848
     *                <p>(Immutable)</p>
4849
     *
4850
     * @psalm-return static<TKey,T>
4851
     * @psalm-mutation-free
4852
     */
4853 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...
4854
    {
4855 7
        $tmpArray = $this->toArray();
4856
4857 7
        \array_pop($tmpArray);
4858
4859 7
        return static::create(
4860 7
            $tmpArray,
4861 7
            $this->iteratorClass,
4862 7
            false
4863
        );
4864
    }
4865
4866
    /**
4867
     * Removes a particular value from an array (numeric or associative).
4868
     *
4869
     * @param mixed $value
4870
     *
4871
     * @return static
4872
     *                <p>(Immutable)</p>
4873
     *
4874
     * @psalm-param  T $value
4875
     * @psalm-return static<TKey,T>
4876
     * @psalm-mutation-free
4877
     */
4878 8
    public function removeValue($value): self
4879
    {
4880 8
        $this->generatorToArray();
4881
4882
        // init
4883 8
        $isSequentialArray = $this->isSequential();
4884
4885 8
        foreach ($this->array as $key => $item) {
4886 7
            if ($item === $value) {
4887 7
                unset($this->array[$key]);
4888
            }
4889
        }
4890
4891 8
        if ($isSequentialArray) {
4892 6
            $this->array = \array_values($this->array);
4893
        }
4894
4895 8
        return static::create(
4896 8
            $this->array,
4897 8
            $this->iteratorClass,
4898 8
            false
4899
        );
4900
    }
4901
4902
    /**
4903
     * Generate array of repeated arrays.
4904
     *
4905
     * @param int $times <p>How many times has to be repeated.</p>
4906
     *
4907
     * @return static
4908
     *                <p>(Immutable)</p>
4909
     *
4910
     * @psalm-return static<TKey,T>
4911
     * @psalm-mutation-free
4912
     */
4913 1
    public function repeat($times): self
4914
    {
4915 1
        if ($times === 0) {
4916 1
            return static::create([], $this->iteratorClass);
4917
        }
4918
4919 1
        return static::create(
4920 1
            \array_fill(0, (int) $times, $this->toArray()),
4921 1
            $this->iteratorClass,
4922 1
            false
4923
        );
4924
    }
4925
4926
    /**
4927
     * Replace a key with a new key/value pair.
4928
     *
4929
     * @param mixed $oldKey
4930
     * @param mixed $newKey
4931
     * @param mixed $newValue
4932
     *
4933
     * @return static
4934
     *                <p>(Immutable)</p>
4935
     *
4936
     * @psalm-return static<TKey,T>
4937
     * @psalm-mutation-free
4938
     */
4939 5
    public function replace($oldKey, $newKey, $newValue): self
4940
    {
4941 5
        $that = clone $this;
4942
4943
        /**
4944
         * @psalm-suppress ImpureMethodCall - object is already cloned
4945
         */
4946 5
        return $that->remove($oldKey)
4947 5
            ->set($newKey, $newValue);
4948
    }
4949
4950
    /**
4951
     * Create an array using the current array as values and the other array as keys.
4952
     *
4953
     * @param array $keys <p>An array of keys.</p>
4954
     *
4955
     * @return static
4956
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
4957
     *
4958
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4959
     * @psalm-return static<TKey,T>
4960
     * @psalm-mutation-free
4961
     */
4962 2
    public function replaceAllKeys(array $keys): self
4963
    {
4964 2
        return static::create(
4965 2
            \array_combine($keys, $this->toArray()),
4966 2
            $this->iteratorClass,
4967 2
            false
4968
        );
4969
    }
4970
4971
    /**
4972
     * Create an array using the current array as keys and the other array as values.
4973
     *
4974
     * @param array $array <p>An array o values.</p>
4975
     *
4976
     * @return static
4977
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
4978
     *
4979
     * @psalm-param  array<mixed,T> $array
4980
     * @psalm-return static<TKey,T>
4981
     * @psalm-mutation-free
4982
     */
4983 2
    public function replaceAllValues(array $array): self
4984
    {
4985 2
        return static::create(
4986 2
            \array_combine($this->array, $array),
4987 2
            $this->iteratorClass,
4988 2
            false
4989
        );
4990
    }
4991
4992
    /**
4993
     * Replace the keys in an array with another set.
4994
     *
4995
     * @param array $keys <p>An array of keys matching the array's size</p>
4996
     *
4997
     * @return static
4998
     *                <p>(Immutable)</p>
4999
     *
5000
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
5001
     * @psalm-return static<TKey,T>
5002
     * @psalm-mutation-free
5003
     */
5004 1
    public function replaceKeys(array $keys): self
5005
    {
5006 1
        $values = \array_values($this->toArray());
5007 1
        $result = \array_combine($keys, $values);
5008
5009 1
        return static::create(
5010 1
            $result,
5011 1
            $this->iteratorClass,
5012 1
            false
5013
        );
5014
    }
5015
5016
    /**
5017
     * Replace the first matched value in an array.
5018
     *
5019
     * @param mixed $search      <p>The value to replace.</p>
5020
     * @param mixed $replacement <p>The value to replace.</p>
5021
     *
5022
     * @return static
5023
     *                <p>(Immutable)</p>
5024
     *
5025
     * @psalm-return static<TKey,T>
5026
     * @psalm-mutation-free
5027
     */
5028 3 View Code Duplication
    public function replaceOneValue($search, $replacement = ''): 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...
5029
    {
5030 3
        $array = $this->toArray();
5031 3
        $key = \array_search($search, $array, true);
5032
5033 3
        if ($key !== false) {
5034 3
            $array[$key] = $replacement;
5035
        }
5036
5037 3
        return static::create(
5038 3
            $array,
5039 3
            $this->iteratorClass,
5040 3
            false
5041
        );
5042
    }
5043
5044
    /**
5045
     * Replace values in the current array.
5046
     *
5047
     * @param mixed $search      <p>The value to replace.</p>
5048
     * @param mixed $replacement <p>What to replace it with.</p>
5049
     *
5050
     * @return static
5051
     *                <p>(Immutable)</p>
5052
     *
5053
     * @psalm-return static<TKey,T>
5054
     * @psalm-mutation-free
5055
     */
5056 1
    public function replaceValues($search, $replacement = ''): self
5057
    {
5058
        /**
5059
         * @psalm-suppress MissingClosureReturnType
5060
         * @psalm-suppress MissingClosureParamType
5061
         */
5062 1
        return $this->each(
5063
            static function ($value) use ($search, $replacement) {
5064 1
                return \str_replace($search, $replacement, $value);
5065 1
            }
5066
        );
5067
    }
5068
5069
    /**
5070
     * Get the last elements from index $from until the end of this array.
5071
     *
5072
     * @param int $from
5073
     *
5074
     * @return static
5075
     *                <p>(Immutable)</p>
5076
     *
5077
     * @psalm-return static<TKey,T>
5078
     * @psalm-mutation-free
5079
     */
5080 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...
5081
    {
5082 15
        $tmpArray = $this->toArray();
5083
5084 15
        return static::create(
5085 15
            \array_splice($tmpArray, $from),
5086 15
            $this->iteratorClass,
5087 15
            false
5088
        );
5089
    }
5090
5091
    /**
5092
     * Return the array in the reverse order.
5093
     *
5094
     * @return $this
5095
     *               <p>(Mutable) Return this Arrayy object.</p>
5096
     *
5097
     * @psalm-return static<TKey,T>
5098
     */
5099 9
    public function reverse(): self
5100
    {
5101 9
        $this->generatorToArray();
5102
5103 9
        $this->array = \array_reverse($this->array);
5104
5105 9
        return $this;
5106
    }
5107
5108
    /**
5109
     * Sort an array in reverse order.
5110
     *
5111
     * @param int $sort_flags [optional] <p>
5112
     *                        You may modify the behavior of the sort using the optional
5113
     *                        parameter sort_flags, for details
5114
     *                        see sort.
5115
     *                        </p>
5116
     *
5117
     * @return $this
5118
     *               <p>(Mutable) Return this Arrayy object.</p>
5119
     *
5120
     * @psalm-return static<TKey,T>
5121
     */
5122 4
    public function rsort(int $sort_flags = 0): self
5123
    {
5124 4
        $this->generatorToArray();
5125
5126 4
        \rsort($this->array, $sort_flags);
5127
5128 4
        return $this;
5129
    }
5130
5131
    /**
5132
     * Sort an array in reverse order.
5133
     *
5134
     * @param int $sort_flags [optional] <p>
5135
     *                        You may modify the behavior of the sort using the optional
5136
     *                        parameter sort_flags, for details
5137
     *                        see sort.
5138
     *                        </p>
5139
     *
5140
     * @return $this
5141
     *               <p>(Immutable) Return this Arrayy object.</p>
5142
     *
5143
     * @psalm-return static<TKey,T>
5144
     * @psalm-mutation-free
5145
     */
5146 4
    public function rsortImmutable(int $sort_flags = 0): self
5147
    {
5148 4
        $that = clone $this;
5149
5150
        /**
5151
         * @psalm-suppress ImpureMethodCall - object is already cloned
5152
         */
5153 4
        $that->rsort($sort_flags);
5154
5155 4
        return $that;
5156
    }
5157
5158
    /**
5159
     * Search for the first index of the current array via $value.
5160
     *
5161
     * @param mixed $value
5162
     *
5163
     * @return false|float|int|string
5164
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
5165
     * @psalm-mutation-free
5166
     */
5167 21
    public function searchIndex($value)
5168
    {
5169 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
5170 20
            if ($value === $valueFromArray) {
5171 10
                return $keyFromArray;
5172
            }
5173
        }
5174
5175 11
        return false;
5176
    }
5177
5178
    /**
5179
     * Search for the value of the current array via $index.
5180
     *
5181
     * @param mixed $index
5182
     *
5183
     * @return static
5184
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
5185
     *
5186
     * @psalm-return static<TKey,T>
5187
     * @psalm-mutation-free
5188
     */
5189 9
    public function searchValue($index): self
5190
    {
5191 9
        $this->generatorToArray();
5192
5193
        // init
5194 9
        $return = [];
5195
5196 9
        if ($this->array === []) {
5197
            return static::create(
5198
                [],
5199
                $this->iteratorClass,
5200
                false
5201
            );
5202
        }
5203
5204
        // php cast "bool"-index into "int"-index
5205 9
        if ((bool) $index === $index) {
5206 1
            $index = (int) $index;
5207
        }
5208
5209 9
        if ($this->offsetExists($index)) {
5210 7
            $return = [$this->array[$index]];
5211
        }
5212
5213 9
        return static::create(
5214 9
            $return,
5215 9
            $this->iteratorClass,
5216 9
            false
5217
        );
5218
    }
5219
5220
    /**
5221
     * Set a value for the current array (optional using dot-notation).
5222
     *
5223
     * @param string $key   <p>The key to set.</p>
5224
     * @param mixed  $value <p>Its value.</p>
5225
     *
5226
     * @return $this
5227
     *               <p>(Mutable) Return this Arrayy object.</p>
5228
     *
5229
     * @psalm-param  TKey $key
5230
     * @psalm-param  T $value
5231
     * @psalm-return static<TKey,T>
5232
     */
5233 28
    public function set($key, $value): self
5234
    {
5235 28
        $this->internalSet($key, $value);
5236
5237 27
        return $this;
5238
    }
5239
5240
    /**
5241
     * Get a value from a array and set it if it was not.
5242
     *
5243
     * WARNING: this method only set the value, if the $key is not already set
5244
     *
5245
     * @param mixed $key      <p>The key</p>
5246
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
5247
     *
5248
     * @return mixed
5249
     *               <p>(Mutable)</p>
5250
     */
5251 11
    public function setAndGet($key, $fallback = null)
5252
    {
5253 11
        $this->generatorToArray();
5254
5255
        // If the key doesn't exist, set it.
5256 11
        if (!$this->has($key)) {
5257 4
            $this->array = $this->set($key, $fallback)->toArray();
5258
        }
5259
5260 11
        return $this->get($key);
5261
    }
5262
5263
    /**
5264
     * Shifts a specified value off the beginning of array.
5265
     *
5266
     * @return mixed
5267
     *               <p>(Mutable) A shifted element from the current array.</p>
5268
     */
5269 5
    public function shift()
5270
    {
5271 5
        $this->generatorToArray();
5272
5273 5
        return \array_shift($this->array);
5274
    }
5275
5276
    /**
5277
     * Shuffle the current array.
5278
     *
5279
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
5280
     * @param array $array  [optional]
5281
     *
5282
     * @return static
5283
     *                <p>(Immutable)</p>
5284
     *
5285
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
5286
     * @psalm-return static<TKey,T>
5287
     *
5288
     * @noinspection BadExceptionsProcessingInspection
5289
     * @noinspection RandomApiMigrationInspection
5290
     * @noinspection NonSecureShuffleUsageInspection
5291
     */
5292 2
    public function shuffle(bool $secure = false, array $array = null): self
5293
    {
5294 2
        if ($array === null) {
5295 2
            $array = $this->toArray(false);
5296
        }
5297
5298 2
        if ($secure !== true) {
5299 2
            \shuffle($array);
5300
        } else {
5301 1
            $size = \count($array, \COUNT_NORMAL);
5302 1
            $keys = \array_keys($array);
5303 1
            for ($i = $size - 1; $i > 0; --$i) {
5304
                try {
5305 1
                    $r = \random_int(0, $i);
5306
                } catch (\Exception $e) {
5307
                    $r = \mt_rand(0, $i);
5308
                }
5309 1
                if ($r !== $i) {
5310
                    $temp = $array[$keys[$r]];
5311
                    $array[$keys[$r]] = $array[$keys[$i]];
5312
                    $array[$keys[$i]] = $temp;
5313
                }
5314
            }
5315
        }
5316
5317 2
        foreach ($array as $key => $value) {
5318
            // check if recursive is needed
5319 2
            if (\is_array($value) === true) {
5320
                $array[$key] = $this->shuffle($secure, $value);
5321
            }
5322
        }
5323
5324 2
        return static::create(
5325 2
            $array,
5326 2
            $this->iteratorClass,
5327 2
            false
5328
        );
5329
    }
5330
5331
    /**
5332
     * Count the values from the current array.
5333
     *
5334
     * alias: for "Arrayy->count()"
5335
     *
5336
     * @param int $mode
5337
     *
5338
     * @return int
5339
     */
5340 20
    public function size(int $mode = \COUNT_NORMAL): int
5341
    {
5342 20
        return $this->count($mode);
5343
    }
5344
5345
    /**
5346
     * Checks whether array has exactly $size items.
5347
     *
5348
     * @param int $size
5349
     *
5350
     * @return bool
5351
     */
5352 1
    public function sizeIs(int $size): bool
5353
    {
5354
        // init
5355 1
        $itemsTempCount = 0;
5356
5357
        /** @noinspection PhpUnusedLocalVariableInspection */
5358
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
5359 1
        foreach ($this->getGeneratorByReference() as &$value) {
5360 1
            ++$itemsTempCount;
5361 1
            if ($itemsTempCount > $size) {
5362 1
                return false;
5363
            }
5364
        }
5365
5366 1
        return $itemsTempCount === $size;
5367
    }
5368
5369
    /**
5370
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
5371
     * smaller than $fromSize.
5372
     *
5373
     * @param int $fromSize
5374
     * @param int $toSize
5375
     *
5376
     * @return bool
5377
     */
5378 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
5379
    {
5380 1
        if ($fromSize > $toSize) {
5381 1
            $tmp = $toSize;
5382 1
            $toSize = $fromSize;
5383 1
            $fromSize = $tmp;
5384
        }
5385
5386
        // init
5387 1
        $itemsTempCount = 0;
5388
5389 1
        foreach ($this->getGenerator() as $key => $value) {
5390 1
            ++$itemsTempCount;
5391 1
            if ($itemsTempCount > $toSize) {
5392 1
                return false;
5393
            }
5394
        }
5395
5396 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
5397
    }
5398
5399
    /**
5400
     * Checks whether array has more than $size items.
5401
     *
5402
     * @param int $size
5403
     *
5404
     * @return bool
5405
     */
5406 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...
5407
    {
5408
        // init
5409 1
        $itemsTempCount = 0;
5410
5411 1
        foreach ($this->getGenerator() as $key => $value) {
5412 1
            ++$itemsTempCount;
5413 1
            if ($itemsTempCount > $size) {
5414 1
                return true;
5415
            }
5416
        }
5417
5418 1
        return $itemsTempCount > $size;
5419
    }
5420
5421
    /**
5422
     * Checks whether array has less than $size items.
5423
     *
5424
     * @param int $size
5425
     *
5426
     * @return bool
5427
     */
5428 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...
5429
    {
5430
        // init
5431 1
        $itemsTempCount = 0;
5432
5433 1
        foreach ($this->getGenerator() as $key => $value) {
5434 1
            ++$itemsTempCount;
5435 1
            if ($itemsTempCount > $size) {
5436 1
                return false;
5437
            }
5438
        }
5439
5440 1
        return $itemsTempCount < $size;
5441
    }
5442
5443
    /**
5444
     * Counts all elements in an array, or something in an object.
5445
     *
5446
     * <p>
5447
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
5448
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
5449
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
5450
     * implemented and used in PHP.
5451
     * </p>
5452
     *
5453
     * @return int
5454
     *             <p>
5455
     *             The number of elements in var, which is
5456
     *             typically an array, since anything else will have one
5457
     *             element.
5458
     *             </p>
5459
     *             <p>
5460
     *             If var is not an array or an object with
5461
     *             implemented Countable interface,
5462
     *             1 will be returned.
5463
     *             There is one exception, if var is &null;,
5464
     *             0 will be returned.
5465
     *             </p>
5466
     *             <p>
5467
     *             Caution: count may return 0 for a variable that isn't set,
5468
     *             but it may also return 0 for a variable that has been initialized with an
5469
     *             empty array. Use isset to test if a variable is set.
5470
     *             </p>
5471
     */
5472 10
    public function sizeRecursive(): int
5473
    {
5474 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
5475
    }
5476
5477
    /**
5478
     * Extract a slice of the array.
5479
     *
5480
     * @param int      $offset       <p>Slice begin index.</p>
5481
     * @param int|null $length       <p>Length of the slice.</p>
5482
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
5483
     *
5484
     * @return static
5485
     *                <p>(Immutable) A slice of the original array with length $length.</p>
5486
     *
5487
     * @psalm-return static<TKey,T>
5488
     * @psalm-mutation-free
5489
     */
5490 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
5491
    {
5492 5
        return static::create(
5493 5
            \array_slice(
5494 5
                $this->toArray(),
5495 5
                $offset,
5496 5
                $length,
5497 5
                $preserveKeys
5498
            ),
5499 5
            $this->iteratorClass,
5500 5
            false
5501
        );
5502
    }
5503
5504
    /**
5505
     * Sort the current array and optional you can keep the keys.
5506
     *
5507
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5508
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5509
     *                              <strong>SORT_NATURAL</strong></p>
5510
     * @param bool       $keepKeys
5511
     *
5512
     * @return static
5513
     *                <p>(Mutable) Return this Arrayy object.</p>
5514
     *
5515
     * @psalm-return static<TKey,T>
5516
     */
5517 20
    public function sort(
5518
        $direction = \SORT_ASC,
5519
        int $strategy = \SORT_REGULAR,
5520
        bool $keepKeys = false
5521
    ): self {
5522 20
        $this->generatorToArray();
5523
5524 20
        return $this->sorting(
5525 20
            $this->array,
5526 20
            $direction,
5527 20
            $strategy,
5528 20
            $keepKeys
5529
        );
5530
    }
5531
5532
    /**
5533
     * Sort the current array and optional you can keep the keys.
5534
     *
5535
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5536
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5537
     *                              <strong>SORT_NATURAL</strong></p>
5538
     * @param bool       $keepKeys
5539
     *
5540
     * @return static
5541
     *                <p>(Immutable) Return this Arrayy object.</p>
5542
     *
5543
     * @psalm-return static<TKey,T>
5544
     */
5545 12
    public function sortImmutable(
5546
        $direction = \SORT_ASC,
5547
        int $strategy = \SORT_REGULAR,
5548
        bool $keepKeys = false
5549
    ): self {
5550 12
        $that = clone $this;
5551
5552 12
        $that->generatorToArray();
5553
5554 12
        return $that->sorting(
5555 12
            $that->array,
5556 12
            $direction,
5557 12
            $strategy,
5558 12
            $keepKeys
5559
        );
5560
    }
5561
5562
    /**
5563
     * Sort the current array by key.
5564
     *
5565
     * @see          http://php.net/manual/en/function.ksort.php
5566
     * @see          http://php.net/manual/en/function.krsort.php
5567
     *
5568
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5569
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5570
     *                              <strong>SORT_NATURAL</strong></p>
5571
     *
5572
     * @return $this
5573
     *               <p>(Mutable) Return this Arrayy object.</p>
5574
     *
5575
     * @psalm-return static<TKey,T>
5576
     */
5577 18
    public function sortKeys(
5578
        $direction = \SORT_ASC,
5579
        int $strategy = \SORT_REGULAR
5580
    ): self {
5581 18
        $this->generatorToArray();
5582
5583 18
        $this->sorterKeys($this->array, $direction, $strategy);
5584
5585 18
        return $this;
5586
    }
5587
5588
    /**
5589
     * Sort the current array by key.
5590
     *
5591
     * @see          http://php.net/manual/en/function.ksort.php
5592
     * @see          http://php.net/manual/en/function.krsort.php
5593
     *
5594
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5595
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5596
     *                              <strong>SORT_NATURAL</strong></p>
5597
     *
5598
     * @return $this
5599
     *               <p>(Immutable) Return this Arrayy object.</p>
5600
     *
5601
     * @psalm-return static<TKey,T>
5602
     * @psalm-mutation-free
5603
     */
5604 8
    public function sortKeysImmutable(
5605
        $direction = \SORT_ASC,
5606
        int $strategy = \SORT_REGULAR
5607
    ): self {
5608 8
        $that = clone $this;
5609
5610
        /**
5611
         * @psalm-suppress ImpureMethodCall - object is already cloned
5612
         */
5613 8
        $that->sortKeys($direction, $strategy);
5614
5615 8
        return $that;
5616
    }
5617
5618
    /**
5619
     * Sort the current array by value.
5620
     *
5621
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5622
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5623
     *                              <strong>SORT_NATURAL</strong></p>
5624
     *
5625
     * @return static
5626
     *                <p>(Mutable)</p>
5627
     *
5628
     * @psalm-return static<TKey,T>
5629
     */
5630 1
    public function sortValueKeepIndex(
5631
        $direction = \SORT_ASC,
5632
        int $strategy = \SORT_REGULAR
5633
    ): self {
5634 1
        return $this->sort($direction, $strategy, true);
5635
    }
5636
5637
    /**
5638
     * Sort the current array by value.
5639
     *
5640
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5641
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5642
     *                              <strong>SORT_NATURAL</strong></p>
5643
     *
5644
     * @return static
5645
     *                <p>(Mutable)</p>
5646
     *
5647
     * @psalm-return static<TKey,T>
5648
     */
5649 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5650
    {
5651 1
        return $this->sort($direction, $strategy, false);
5652
    }
5653
5654
    /**
5655
     * Sort a array by value, by a closure or by a property.
5656
     *
5657
     * - If the sorter is null, the array is sorted naturally.
5658
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
5659
     *
5660
     * @param callable|string|null $sorter
5661
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
5662
     *                                        <strong>SORT_DESC</strong></p>
5663
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5664
     *                                        <strong>SORT_NATURAL</strong></p>
5665
     *
5666
     * @return static
5667
     *                <p>(Immutable)</p>
5668
     *
5669
     * @psalm-return static<TKey,T>
5670
     * @psalm-mutation-free
5671
     */
5672 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5673
    {
5674 1
        $array = $this->toArray();
5675 1
        $direction = $this->getDirection($direction);
5676
5677
        // Transform all values into their results.
5678 1
        if ($sorter) {
5679 1
            $arrayy = static::create(
5680 1
                $array,
5681 1
                $this->iteratorClass,
5682 1
                false
5683
            );
5684
5685
            /**
5686
             * @psalm-suppress MissingClosureReturnType
5687
             * @psalm-suppress MissingClosureParamType
5688
             */
5689 1
            $results = $arrayy->each(
5690
                function ($value) use ($sorter) {
5691 1
                    if (\is_callable($sorter) === true) {
5692 1
                        return $sorter($value);
5693
                    }
5694
5695 1
                    return $this->get($sorter);
5696 1
                }
5697
            );
5698
5699 1
            $results = $results->toArray();
5700
        } else {
5701 1
            $results = $array;
5702
        }
5703
5704
        // Sort by the results and replace by original values
5705 1
        \array_multisort($results, $direction, $strategy, $array);
5706
5707 1
        return static::create(
5708 1
            $array,
5709 1
            $this->iteratorClass,
5710 1
            false
5711
        );
5712
    }
5713
5714
    /**
5715
     * @param int      $offset
5716
     * @param int|null $length
5717
     * @param array    $replacement
5718
     *
5719
     * @return static
5720
     *                <p>(Immutable)</p>
5721
     *
5722
     * @psalm-param  array<mixed,mixed>|array<mixed,T> $replacement
5723
     * @psalm-return static<TKey,T>
5724
     * @psalm-mutation-free
5725
     */
5726 1
    public function splice(int $offset, int $length = null, $replacement = []): self
5727
    {
5728 1
        $tmpArray = $this->toArray();
5729
5730 1
        \array_splice(
5731 1
            $tmpArray,
5732 1
            $offset,
5733 1
            $length ?? $this->count(),
5734 1
            $replacement
5735
        );
5736
5737 1
        return static::create(
5738 1
            $tmpArray,
5739 1
            $this->iteratorClass,
5740 1
            false
5741
        );
5742
    }
5743
5744
    /**
5745
     * Split an array in the given amount of pieces.
5746
     *
5747
     * @param int  $numberOfPieces
5748
     * @param bool $keepKeys
5749
     *
5750
     * @return static
5751
     *                <p>(Immutable)</p>
5752
     *
5753
     * @psalm-return static<TKey,T>
5754
     * @psalm-mutation-free
5755
     */
5756 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
5757
    {
5758 1
        $this->generatorToArray();
5759
5760 1
        $count = $this->count();
5761
5762 1
        if ($count === 0) {
5763 1
            $result = [];
5764
        } else {
5765 1
            $splitSize = (int) \ceil($count / $numberOfPieces);
5766 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
5767
        }
5768
5769 1
        return static::create(
5770 1
            $result,
5771 1
            $this->iteratorClass,
5772 1
            false
5773
        );
5774
    }
5775
5776
    /**
5777
     * Stripe all empty items.
5778
     *
5779
     * @return static
5780
     *                <p>(Immutable)</p>
5781
     *
5782
     * @psalm-return static<TKey,T>
5783
     * @psalm-mutation-free
5784
     */
5785 1
    public function stripEmpty(): self
5786
    {
5787 1
        return $this->filter(
5788
            static function ($item) {
5789 1
                if ($item === null) {
5790 1
                    return false;
5791
                }
5792
5793 1
                return (bool) \trim((string) $item);
5794 1
            }
5795
        );
5796
    }
5797
5798
    /**
5799
     * Swap two values between positions by key.
5800
     *
5801
     * @param int|string $swapA <p>a key in the array</p>
5802
     * @param int|string $swapB <p>a key in the array</p>
5803
     *
5804
     * @return static
5805
     *                <p>(Immutable)</p>
5806
     *
5807
     * @psalm-return static<TKey,T>
5808
     * @psalm-mutation-free
5809
     */
5810 1
    public function swap($swapA, $swapB): self
5811
    {
5812 1
        $array = $this->toArray();
5813
5814 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
5815
5816 1
        return static::create(
5817 1
            $array,
5818 1
            $this->iteratorClass,
5819 1
            false
5820
        );
5821
    }
5822
5823
    /**
5824
     * Get the current array from the "Arrayy"-object.
5825
     * alias for "getArray()"
5826
     *
5827
     * @param bool $convertAllArrayyElements <p>
5828
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5829
     *                                       </p>
5830
     * @param bool $preserveKeys             <p>
5831
     *                                       e.g.: A generator maybe return the same key more then once,
5832
     *                                       so maybe you will ignore the keys.
5833
     *                                       </p>
5834
     *
5835
     * @return array
5836
     *
5837
     * @psalm-return array<mixed,mixed>|array<TKey,T>
5838
     * @psalm-mutation-free
5839
     */
5840 945
    public function toArray(
5841
        bool $convertAllArrayyElements = false,
5842
        bool $preserveKeys = true
5843
    ): array {
5844
        // init
5845 945
        $array = [];
5846
5847 945
        if ($convertAllArrayyElements) {
5848 2
            foreach ($this->getGenerator() as $key => $value) {
5849 2
                if ($value instanceof self) {
5850 1
                    $value = $value->toArray(true);
5851
                }
5852
5853 2
                if ($preserveKeys) {
5854 1
                    $array[$key] = $value;
5855
                } else {
5856 1
                    $array[] = $value;
5857
                }
5858
            }
5859
        } else {
5860 945
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
5861
        }
5862
5863 945
        return $array;
5864
    }
5865
5866
    /**
5867
     * Get the current array from the "Arrayy"-object as list.
5868
     *
5869
     * @param bool $convertAllArrayyElements <p>
5870
     *                                       Convert all Child-"Arrayy" objects also to arrays.
5871
     *                                       </p>
5872
     *
5873
     * @return array
5874
     *
5875
     * @psalm-return list<array<TKey,T>>
5876
     * @psalm-mutation-free
5877
     */
5878 1
    public function toList(bool $convertAllArrayyElements = false): array
5879
    {
5880 1
        return $this->toArray(
5881 1
            $convertAllArrayyElements,
5882 1
            false
5883
        );
5884
    }
5885
5886
    /**
5887
     * Convert the current array to JSON.
5888
     *
5889
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
5890
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
5891
     *
5892
     * @return string
5893
     */
5894 12
    public function toJson(int $options = 0, int $depth = 512): string
5895
    {
5896 12
        $return = \json_encode($this->toArray(), $options, $depth);
5897 12
        if ($return === false) {
5898
            return '';
5899
        }
5900
5901 12
        return $return;
5902
    }
5903
5904
    /**
5905
     * @param string[]|null $items  [optional]
5906
     * @param string[]      $helper [optional]
5907
     *
5908
     * @return static|static[]
5909
     *
5910
     * @psalm-return static<int, static<TKey,T>>
5911
     */
5912 1
    public function toPermutation(array $items = null, array $helper = []): self
5913
    {
5914
        // init
5915 1
        $return = [];
5916
5917 1
        if ($items === null) {
5918 1
            $items = $this->toArray();
5919
        }
5920
5921 1
        if (empty($items)) {
5922 1
            $return[] = $helper;
5923
        } else {
5924 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
5925 1
                $new_items = $items;
5926 1
                $new_helper = $helper;
5927 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
5928
                /** @noinspection PhpSillyAssignmentInspection */
5929
                /** @var string[] $new_items */
5930 1
                $new_items = $new_items;
0 ignored issues
show
Bug introduced by
Why assign $new_items to itself?

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
5931 1
                \array_unshift($new_helper, $tmp_helper);
5932
                /** @noinspection SlowArrayOperationsInLoopInspection */
5933 1
                $return = \array_merge(
5934 1
                    $return,
5935 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
5936
                );
5937
            }
5938
        }
5939
5940 1
        return static::create(
5941 1
            $return,
5942 1
            $this->iteratorClass,
5943 1
            false
5944
        );
5945
    }
5946
5947
    /**
5948
     * Implodes array to a string with specified separator.
5949
     *
5950
     * @param string $separator [optional] <p>The element's separator.</p>
5951
     *
5952
     * @return string
5953
     *                <p>The string representation of array, separated by ",".</p>
5954
     */
5955 19
    public function toString(string $separator = ','): string
5956
    {
5957 19
        return $this->implode($separator);
5958
    }
5959
5960
    /**
5961
     * Return a duplicate free copy of the current array.
5962
     *
5963
     * @return $this
5964
     *               <p>(Mutable)</p>
5965
     *
5966
     * @psalm-return static<TKey,T>
5967
     */
5968 13
    public function unique(): self
5969
    {
5970
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5971
5972
        /**
5973
         * @psalm-suppress MissingClosureReturnType
5974
         * @psalm-suppress MissingClosureParamType
5975
         */
5976 13
        $this->array = $this->reduce(
5977
            static function ($resultArray, $value) {
5978 12
                if (!\in_array($value, $resultArray, true)) {
5979 12
                    $resultArray[] = $value;
5980
                }
5981
5982 12
                return $resultArray;
5983 13
            },
5984 13
            []
5985 13
        )->toArray();
5986 13
        $this->generator = null;
5987
5988 13
        return $this;
5989
    }
5990
5991
    /**
5992
     * Return a duplicate free copy of the current array. (with the old keys)
5993
     *
5994
     * @return $this
5995
     *               <p>(Mutable)</p>
5996
     *
5997
     * @psalm-return static<TKey,T>
5998
     */
5999 11
    public function uniqueKeepIndex(): self
6000
    {
6001
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6002
6003
        // init
6004 11
        $array = $this->toArray();
6005
6006
        /**
6007
         * @psalm-suppress MissingClosureReturnType
6008
         * @psalm-suppress MissingClosureParamType
6009
         */
6010 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...
6011 11
            \array_keys($array),
6012
            static function ($resultArray, $key) use ($array) {
6013 10
                if (!\in_array($array[$key], $resultArray, true)) {
6014 10
                    $resultArray[$key] = $array[$key];
6015
                }
6016
6017 10
                return $resultArray;
6018 11
            },
6019 11
            []
6020
        );
6021 11
        $this->generator = null;
6022
6023 11
        return $this;
6024
    }
6025
6026
    /**
6027
     * alias: for "Arrayy->unique()"
6028
     *
6029
     * @return static
6030
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
6031
     *
6032
     * @see          Arrayy::unique()
6033
     *
6034
     * @psalm-return static<TKey,T>
6035
     */
6036 10
    public function uniqueNewIndex(): self
6037
    {
6038 10
        return $this->unique();
6039
    }
6040
6041
    /**
6042
     * Prepends one or more values to the beginning of array at once.
6043
     *
6044
     * @param array ...$args
6045
     *
6046
     * @return $this
6047
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
6048
     *
6049
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
6050
     * @psalm-return static<TKey,T>
6051
     */
6052 4
    public function unshift(...$args): self
6053
    {
6054 4
        $this->generatorToArray();
6055
6056 4
        \array_unshift($this->array, ...$args);
6057
6058 4
        return $this;
6059
    }
6060
6061
    /**
6062
     * Tests whether the given closure return something valid for all elements of this array.
6063
     *
6064
     * @param \Closure $closure the predicate
6065
     *
6066
     * @return bool
6067
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
6068
     */
6069 1 View Code Duplication
    public function validate(\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...
6070
    {
6071 1
        foreach ($this->getGenerator() as $key => $value) {
6072 1
            if (!$closure($value, $key)) {
6073 1
                return false;
6074
            }
6075
        }
6076
6077 1
        return true;
6078
    }
6079
6080
    /**
6081
     * Get all values from a array.
6082
     *
6083
     * @return static
6084
     *                <p>(Immutable)</p>
6085
     *
6086
     * @psalm-return static<TKey,T>
6087
     * @psalm-mutation-free
6088
     */
6089 2
    public function values(): self
6090
    {
6091 2
        return static::create(
6092
            function () {
6093
                /** @noinspection YieldFromCanBeUsedInspection */
6094 2
                foreach ($this->getGenerator() as $value) {
6095 2
                    yield $value;
6096
                }
6097 2
            },
6098 2
            $this->iteratorClass,
6099 2
            false
6100
        );
6101
    }
6102
6103
    /**
6104
     * Apply the given function to every element in the array, discarding the results.
6105
     *
6106
     * @param callable $callable
6107
     * @param bool     $recursive [optional] <p>Whether array will be walked recursively or no</p>
6108
     * @param mixed    $userData  [optional] <p>
6109
     *                            If the optional $userData parameter is supplied,
6110
     *                            it will be passed as the third parameter to the $callable.
6111
     *                            </p>
6112
     *
6113
     * @return $this
6114
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
6115
     *
6116
     * @psalm-return static<TKey,T>
6117
     */
6118 12
    public function walk($callable, bool $recursive = false, $userData = self::ARRAYY_HELPER_WALK): self
6119
    {
6120 12
        $this->generatorToArray();
6121
6122 12
        if ($this->array !== []) {
6123 10
            if ($recursive === true) {
6124 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6125
                    \array_walk_recursive($this->array, $callable, $userData);
6126
                } else {
6127 5
                    \array_walk_recursive($this->array, $callable);
6128
                }
6129
            } else {
6130 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6131
                    \array_walk($this->array, $callable, $userData);
6132
                } else {
6133 5
                    \array_walk($this->array, $callable);
6134
                }
6135
            }
6136
        }
6137
6138 12
        return $this;
6139
    }
6140
6141
    /**
6142
     * Returns a collection of matching items.
6143
     *
6144
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
6145
     * @param mixed  $value                 the value to match
6146
     *
6147
     * @throws \InvalidArgumentException if property or method is not defined
6148
     *
6149
     * @return static
6150
     *
6151
     * @psalm-param  T $value
6152
     * @psalm-return static<TKey,T>
6153
     */
6154 1
    public function where(string $keyOrPropertyOrMethod, $value): self
6155
    {
6156 1
        return $this->filter(
6157
            function ($item) use ($keyOrPropertyOrMethod, $value) {
6158 1
                $accessorValue = $this->extractValue(
6159 1
                    $item,
6160 1
                    $keyOrPropertyOrMethod
6161
                );
6162
6163 1
                return $accessorValue === $value;
6164 1
            }
6165
        );
6166
    }
6167
6168
    /**
6169
     * Convert an array into a object.
6170
     *
6171
     * @param array $array
6172
     *
6173
     * @return \stdClass
6174
     *
6175
     * @psalm-param array<mixed,mixed> $array
6176
     */
6177 4
    final protected static function arrayToObject(array $array = []): \stdClass
6178
    {
6179
        // init
6180 4
        $object = new \stdClass();
6181
6182 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
6183 1
            return $object;
6184
        }
6185
6186 3
        foreach ($array as $name => $value) {
6187 3
            if (\is_array($value) === true) {
6188 1
                $object->{$name} = static::arrayToObject($value);
6189
            } else {
6190 3
                $object->{$name} = $value;
6191
            }
6192
        }
6193
6194 3
        return $object;
6195
    }
6196
6197
    /**
6198
     * @param array|\Generator|null $input         <p>
6199
     *                                             An array containing keys to return.
6200
     *                                             </p>
6201
     * @param mixed|null            $search_values [optional] <p>
6202
     *                                             If specified, then only keys containing these values are returned.
6203
     *                                             </p>
6204
     * @param bool                  $strict        [optional] <p>
6205
     *                                             Determines if strict comparison (===) should be used during the
6206
     *                                             search.
6207
     *                                             </p>
6208
     *
6209
     * @return array
6210
     *               <p>an array of all the keys in input</p>
6211
     *
6212
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
6213
     * @psalm-return array<TKey|mixed>
6214
     * @psalm-mutation-free
6215
     */
6216 11
    protected function array_keys_recursive(
6217
        $input = null,
6218
        $search_values = null,
6219
        bool $strict = true
6220
    ): array {
6221
        // init
6222 11
        $keys = [];
6223 11
        $keysTmp = [];
6224
6225 11
        if ($input === null) {
6226 4
            $input = $this->getGenerator();
6227
        }
6228
6229 11
        if ($search_values === null) {
6230 11
            foreach ($input as $key => $value) {
6231 11
                $keys[] = $key;
6232
6233
                // check if recursive is needed
6234 11
                if (\is_array($value) === true) {
6235 4
                    $keysTmp[] = $this->array_keys_recursive($value);
6236
                }
6237
            }
6238
        } else {
6239 1
            $is_array_tmp = \is_array($search_values);
6240
6241 1
            foreach ($input as $key => $value) {
6242 View Code Duplication
                if (
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...
6243
                    (
6244 1
                        $is_array_tmp === false
6245
                        &&
6246 1
                        $strict === true
6247
                        &&
6248 1
                        $search_values === $value
6249
                    )
6250
                    ||
6251
                    (
6252 1
                        $is_array_tmp === false
6253
                        &&
6254 1
                        $strict === false
6255
                        &&
6256 1
                        $search_values == $value
6257
                    )
6258
                    ||
6259
                    (
6260 1
                        $is_array_tmp === true
6261
                        &&
6262 1
                        \in_array($value, $search_values, $strict)
6263
                    )
6264
                ) {
6265 1
                    $keys[] = $key;
6266
                }
6267
6268
                // check if recursive is needed
6269 1
                if (\is_array($value) === true) {
6270 1
                    $keysTmp[] = $this->array_keys_recursive($value);
6271
                }
6272
            }
6273
        }
6274
6275 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
6276
    }
6277
6278
    /**
6279
     * @param mixed      $path
6280
     * @param callable   $callable
6281
     * @param array|null $currentOffset
6282
     *
6283
     * @return void
6284
     *
6285
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
6286
     * @psalm-mutation-free
6287
     */
6288 10
    protected function callAtPath($path, $callable, &$currentOffset = null)
6289
    {
6290 10
        $this->generatorToArray();
6291
6292 10
        if ($currentOffset === null) {
6293 10
            $currentOffset = &$this->array;
6294
        }
6295
6296 10
        $explodedPath = \explode($this->pathSeparator, $path);
6297 10
        if ($explodedPath === false) {
6298
            return;
6299
        }
6300
6301 10
        $nextPath = \array_shift($explodedPath);
6302
6303 10
        if (!isset($currentOffset[$nextPath])) {
6304 1
            return;
6305
        }
6306
6307 9
        if (!empty($explodedPath)) {
6308 1
            $this->callAtPath(
6309 1
                \implode($this->pathSeparator, $explodedPath),
6310 1
                $callable,
6311 1
                $currentOffset[$nextPath]
6312
            );
6313
        } else {
6314 9
            $callable($currentOffset[$nextPath]);
6315
        }
6316 9
    }
6317
6318
    /**
6319
     * Extracts the value of the given property or method from the object.
6320
     *
6321
     * @param static $object                <p>The object to extract the value from.</p>
6322
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
6323
     *                                      value should be extracted.</p>
6324
     *
6325
     * @throws \InvalidArgumentException if the method or property is not defined
6326
     *
6327
     * @return mixed
6328
     *               <p>The value extracted from the specified property or method.</p>
6329
     *
6330
     * @psalm-param self<TKey,T> $object
6331
     */
6332 2
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
6333
    {
6334 2
        if (isset($object[$keyOrPropertyOrMethod])) {
6335 2
            $return = $object->get($keyOrPropertyOrMethod);
6336
6337 2
            if ($return instanceof self) {
6338 1
                return $return->toArray();
6339
            }
6340
6341 1
            return $return;
6342
        }
6343
6344
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
6345
            return $object->{$keyOrPropertyOrMethod};
6346
        }
6347
6348
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
6349
            return $object->{$keyOrPropertyOrMethod}();
6350
        }
6351
6352
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
6353
    }
6354
6355
    /**
6356
     * create a fallback for array
6357
     *
6358
     * 1. use the current array, if it's a array
6359
     * 2. fallback to empty array, if there is nothing
6360
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
6361
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
6362
     * 5. call "__toArray()" on object, if the method exists
6363
     * 6. cast a string or object with "__toString()" into an array
6364
     * 7. throw a "InvalidArgumentException"-Exception
6365
     *
6366
     * @param mixed $data
6367
     *
6368
     * @throws \InvalidArgumentException
6369
     *
6370
     * @return array
6371
     *
6372
     * @psalm-return array<mixed,mixed>|array<TKey,T>
6373
     */
6374 1198
    protected function fallbackForArray(&$data): array
6375
    {
6376 1198
        $data = $this->internalGetArray($data);
6377
6378 1198
        if ($data === null) {
6379 2
            throw new \InvalidArgumentException('Passed value should be a array');
6380
        }
6381
6382 1196
        return $data;
6383
    }
6384
6385
    /**
6386
     * @param bool $preserveKeys <p>
6387
     *                           e.g.: A generator maybe return the same key more then once,
6388
     *                           so maybe you will ignore the keys.
6389
     *                           </p>
6390
     *
6391
     * @return bool
6392
     *
6393
     * @noinspection ReturnTypeCanBeDeclaredInspection
6394
     * @psalm-mutation-free :/
6395
     */
6396 1110
    protected function generatorToArray(bool $preserveKeys = true)
6397
    {
6398 1110
        if ($this->generator) {
6399 2
            $this->array = $this->toArray(false, $preserveKeys);
6400 2
            $this->generator = null;
6401
6402 2
            return true;
6403
        }
6404
6405 1110
        return false;
6406
    }
6407
6408
    /**
6409
     * Get correct PHP constant for direction.
6410
     *
6411
     * @param int|string $direction
6412
     *
6413
     * @return int
6414
     * @psalm-mutation-free
6415
     */
6416 43
    protected function getDirection($direction): int
6417
    {
6418 43
        if ((string) $direction === $direction) {
6419 10
            $direction = \strtolower($direction);
6420
6421 10
            if ($direction === 'desc') {
6422 2
                $direction = \SORT_DESC;
6423
            } else {
6424 8
                $direction = \SORT_ASC;
6425
            }
6426
        }
6427
6428
        if (
6429 43
            $direction !== \SORT_DESC
6430
            &&
6431 43
            $direction !== \SORT_ASC
6432
        ) {
6433
            $direction = \SORT_ASC;
6434
        }
6435
6436 43
        return $direction;
6437
    }
6438
6439
    /**
6440
     * @return TypeCheckInterface[]
6441
     *
6442
     * @noinspection ReturnTypeCanBeDeclaredInspection
6443
     */
6444 22
    protected function getPropertiesFromPhpDoc()
6445
    {
6446 22
        static $PROPERTY_CACHE = [];
6447 22
        $cacheKey = 'Class::' . static::class;
6448
6449 22
        if (isset($PROPERTY_CACHE[$cacheKey])) {
6450 21
            return $PROPERTY_CACHE[$cacheKey];
6451
        }
6452
6453
        // init
6454 3
        $properties = [];
6455
6456 3
        $reflector = new \ReflectionClass($this);
6457 3
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
6458 3
        $docComment = $reflector->getDocComment();
6459 3
        if ($docComment) {
6460 2
            $docblock = $factory->create($docComment);
6461
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
6462 2
            foreach ($docblock->getTagsByName('property') as $tag) {
6463 2
                $typeName = $tag->getVariableName();
6464 2
                if ($typeName !== null) {
6465 2
                    $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
6466 2
                    if ($typeCheckPhpDoc !== null) {
6467 2
                        $properties[$typeName] = $typeCheckPhpDoc;
6468
                    }
6469
                }
6470
            }
6471
        }
6472
6473 3
        return $PROPERTY_CACHE[$cacheKey] = $properties;
6474
    }
6475
6476
    /**
6477
     * @param mixed $glue
6478
     * @param mixed $pieces
6479
     * @param bool  $useKeys
6480
     *
6481
     * @return string
6482
     * @psalm-mutation-free
6483
     */
6484 36
    protected function implode_recursive(
6485
        $glue = '',
6486
        $pieces = [],
6487
        bool $useKeys = false
6488
    ): string {
6489 36
        if ($pieces instanceof self) {
6490 1
            $pieces = $pieces->toArray();
6491
        }
6492
6493 36
        if (\is_array($pieces) === true) {
6494 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
6495 36
            $pieces_count_not_zero = $pieces_count > 0;
6496
6497 36
            return \implode(
6498 36
                $glue,
6499 36
                \array_map(
6500 36
                    [$this, 'implode_recursive'],
6501 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
6502 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
6503
                )
6504
            );
6505
        }
6506
6507
        if (
6508 36
            \is_scalar($pieces) === true
6509
            ||
6510 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
6511
        ) {
6512 32
            return (string) $pieces;
6513
        }
6514
6515 8
        return '';
6516
    }
6517
6518
    /**
6519
     * @param mixed                 $needle   <p>
6520
     *                                        The searched value.
6521
     *                                        </p>
6522
     *                                        <p>
6523
     *                                        If needle is a string, the comparison is done
6524
     *                                        in a case-sensitive manner.
6525
     *                                        </p>
6526
     * @param array|\Generator|null $haystack <p>
6527
     *                                        The array.
6528
     *                                        </p>
6529
     * @param bool                  $strict   [optional] <p>
6530
     *                                        If the third parameter strict is set to true
6531
     *                                        then the in_array function will also check the
6532
     *                                        types of the
6533
     *                                        needle in the haystack.
6534
     *                                        </p>
6535
     *
6536
     * @return bool
6537
     *              <p>true if needle is found in the array, false otherwise</p>
6538
     *
6539
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
6540
     * @psalm-mutation-free
6541
     */
6542 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
6543
    {
6544 18
        if ($haystack === null) {
6545
            $haystack = $this->getGenerator();
6546
        }
6547
6548 18
        foreach ($haystack as $item) {
6549 14
            if (\is_array($item) === true) {
6550 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
6551
            } else {
6552
                /** @noinspection NestedPositiveIfStatementsInspection */
6553 14
                if ($strict === true) {
6554 14
                    $returnTmp = $item === $needle;
6555
                } else {
6556
                    $returnTmp = $item == $needle;
6557
                }
6558
            }
6559
6560 14
            if ($returnTmp === true) {
6561 10
                return true;
6562
            }
6563
        }
6564
6565 8
        return false;
6566
    }
6567
6568
    /**
6569
     * @param mixed $data
6570
     *
6571
     * @return array|null
6572
     *
6573
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
6574
     */
6575 1198
    protected function internalGetArray(&$data)
6576
    {
6577 1198
        if (\is_array($data) === true) {
6578 1192
            return $data;
6579
        }
6580
6581 56
        if (!$data) {
6582 6
            return [];
6583
        }
6584
6585 55
        if (\is_object($data) === true) {
6586 49
            if ($data instanceof \ArrayObject) {
6587 5
                return $data->getArrayCopy();
6588
            }
6589
6590 45
            if ($data instanceof \Generator) {
6591
                return static::createFromGeneratorImmutable($data)->toArray();
6592
            }
6593
6594 45
            if ($data instanceof \Traversable) {
6595
                return static::createFromObject($data)->toArray();
6596
            }
6597
6598 45
            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...
6599
                return (array) $data->jsonSerialize();
6600
            }
6601
6602 45
            if (\method_exists($data, '__toArray')) {
6603
                return (array) $data->__toArray();
6604
            }
6605
6606 45
            if (\method_exists($data, '__toString')) {
6607
                return [(string) $data];
6608
            }
6609
        }
6610
6611 51
        if (\is_callable($data)) {
6612
            /**
6613
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
6614
             */
6615 43
            $this->generator = new ArrayyRewindableGenerator($data);
6616
6617 43
            return [];
6618
        }
6619
6620 10
        if (\is_scalar($data)) {
6621 8
            return [$data];
6622
        }
6623
6624 2
        return null;
6625
    }
6626
6627
    /**
6628
     * Internal mechanics of remove method.
6629
     *
6630
     * @param mixed $key
6631
     *
6632
     * @return bool
6633
     */
6634 21
    protected function internalRemove($key): bool
6635
    {
6636 21
        $this->generatorToArray();
6637
6638
        if (
6639 21
            $this->pathSeparator
6640
            &&
6641 21
            (string) $key === $key
6642
            &&
6643 21
            \strpos($key, $this->pathSeparator) !== false
6644
        ) {
6645
            $path = \explode($this->pathSeparator, (string) $key);
6646
6647
            if ($path !== false) {
6648
                // crawl though the keys
6649
                while (\count($path, \COUNT_NORMAL) > 1) {
6650
                    $key = \array_shift($path);
6651
6652
                    if (!$this->has($key)) {
6653
                        return false;
6654
                    }
6655
6656
                    $this->array = &$this->array[$key];
6657
                }
6658
6659
                $key = \array_shift($path);
6660
            }
6661
        }
6662
6663 21
        unset($this->array[$key]);
6664
6665 21
        return true;
6666
    }
6667
6668
    /**
6669
     * Internal mechanic of set method.
6670
     *
6671
     * @param int|string|null $key
6672
     * @param mixed           $value
6673
     * @param bool            $checkProperties
6674
     *
6675
     * @return bool
6676
     */
6677 1048
    protected function internalSet(
6678
        $key,
6679
        &$value,
6680
        bool $checkProperties = true
6681
    ): bool {
6682
        if (
6683 1048
            $checkProperties === true
6684
            &&
6685 1048
            $this->properties !== []
6686
        ) {
6687 109
            $this->checkType($key, $value);
6688
        }
6689
6690 1046
        if ($key === null) {
6691
            return false;
6692
        }
6693
6694 1046
        $this->generatorToArray();
6695
6696
        /** @psalm-var array<int|string,mixed> $array */
6697 1046
        $array = &$this->array;
6698
6699
        /**
6700
         * https://github.com/vimeo/psalm/issues/2536
6701
         *
6702
         * @psalm-suppress PossiblyInvalidArgument
6703
         * @psalm-suppress InvalidScalarArgument
6704
         */
6705
        if (
6706 1046
            $this->pathSeparator
6707
            &&
6708 1046
            (string) $key === $key
6709
            &&
6710 1046
            \strpos($key, $this->pathSeparator) !== false
6711
        ) {
6712 9
            $path = \explode($this->pathSeparator, (string) $key);
6713
6714 9
            if ($path !== false) {
6715
                // crawl through the keys
6716 9
                while (\count($path, \COUNT_NORMAL) > 1) {
6717 9
                    $key = \array_shift($path);
6718
6719 9
                    $array = &$array[$key];
6720
                }
6721
6722 9
                $key = \array_shift($path);
6723
            }
6724
        }
6725
6726 1046
        if ($array === null) {
6727 4
            $array = [];
6728 1043
        } elseif (!\is_array($array)) {
6729 1
            throw new \RuntimeException('Can not set value at this path "' . $key . '" because (' . \gettype($array) . ')"' . \print_r($array, true) . '" is not an array.');
6730
        }
6731
6732 1046
        $array[$key] = $value;
6733
6734 1046
        return true;
6735
    }
6736
6737
    /**
6738
     * Convert a object into an array.
6739
     *
6740
     * @param mixed|object $object
6741
     *
6742
     * @return array|mixed
6743
     *
6744
     * @psalm-mutation-free
6745
     */
6746 5
    protected static function objectToArray($object)
6747
    {
6748 5
        if (!\is_object($object)) {
6749 4
            return $object;
6750
        }
6751
6752 5
        $object = \get_object_vars($object);
6753
6754
        /**
6755
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
6756
         */
6757 5
        return \array_map(['static', 'objectToArray'], $object);
6758
    }
6759
6760
    /**
6761
     * @param array $data
6762
     * @param bool  $checkPropertiesInConstructor
6763
     *
6764
     * @return void
6765
     *
6766
     * @psalm-param array<mixed,T> $data
6767
     */
6768 1196
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
6769
    {
6770 1196
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
6771
                                        &&
6772 1196
                                        $checkPropertiesInConstructor === true;
6773
6774 1196
        if ($this->properties !== []) {
6775 98
            foreach ($data as $key => &$valueInner) {
6776 98
                $this->internalSet(
6777 98
                    $key,
6778 98
                    $valueInner,
6779 98
                    $checkPropertiesInConstructor
6780
                );
6781
            }
6782
        } else {
6783
            if (
6784 1117
                $this->checkPropertyTypes === true
6785
                ||
6786 1117
                $checkPropertiesInConstructor === true
6787
            ) {
6788 21
                $this->properties = $this->getPropertiesFromPhpDoc();
6789
            }
6790
6791
            /** @var TypeCheckInterface[] $properties */
6792 1117
            $properties = $this->properties;
6793
6794
            if (
6795 1117
                $this->checkPropertiesMismatchInConstructor === true
6796
                &&
6797 1117
                \count($data) !== 0
6798
                &&
6799 1117
                \count(\array_diff_key($properties, $data)) > 0
6800
            ) {
6801 1
                throw new \TypeError('Property mismatch - input: ' . \print_r(\array_keys($data), true) . ' | expected: ' . \print_r(\array_keys($properties), true));
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'Property mismatch - inp...eys($properties), true).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
6802
            }
6803
6804 1116
            foreach ($data as $key => &$valueInner) {
6805 945
                $this->internalSet(
6806 945
                    $key,
6807 945
                    $valueInner,
6808 945
                    $checkPropertiesInConstructor
6809
                );
6810
            }
6811
        }
6812 1189
    }
6813
6814
    /**
6815
     * sorting keys
6816
     *
6817
     * @param array      $elements
6818
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6819
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6820
     *                              <strong>SORT_NATURAL</strong></p>
6821
     *
6822
     * @return $this
6823
     *               <p>(Mutable) Return this Arrayy object.</p>
6824
     *
6825
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6826
     * @psalm-return static<TKey,T>
6827
     */
6828 18
    protected function sorterKeys(
6829
        array &$elements,
6830
        $direction = \SORT_ASC,
6831
        int $strategy = \SORT_REGULAR
6832
    ): self {
6833 18
        $direction = $this->getDirection($direction);
6834
6835 18
        switch ($direction) {
6836 18
            case 'desc':
6837
            case \SORT_DESC:
6838 6
                \krsort($elements, $strategy);
6839
6840 6
                break;
6841 13
            case 'asc':
6842 13
            case \SORT_ASC:
6843
            default:
6844 13
                \ksort($elements, $strategy);
6845
        }
6846
6847 18
        return $this;
6848
    }
6849
6850
    /**
6851
     * @param array      $elements  <p>Warning: used as reference</p>
6852
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6853
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6854
     *                              <strong>SORT_NATURAL</strong></p>
6855
     * @param bool       $keepKeys
6856
     *
6857
     * @return $this
6858
     *               <p>(Mutable) Return this Arrayy object.</p>
6859
     *
6860
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
6861
     * @psalm-return static<TKey,T>
6862
     */
6863 24
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
6864
    {
6865 24
        $direction = $this->getDirection($direction);
6866
6867 24
        if (!$strategy) {
6868 24
            $strategy = \SORT_REGULAR;
6869
        }
6870
6871 24
        switch ($direction) {
6872 24
            case 'desc':
6873
            case \SORT_DESC:
6874 13
                if ($keepKeys) {
6875 9
                    \arsort($elements, $strategy);
6876
                } else {
6877 4
                    \rsort($elements, $strategy);
6878
                }
6879
6880 13
                break;
6881 11
            case 'asc':
6882 11
            case \SORT_ASC:
6883
            default:
6884 11
                if ($keepKeys) {
6885 4
                    \asort($elements, $strategy);
6886
                } else {
6887 7
                    \sort($elements, $strategy);
6888
                }
6889
        }
6890
6891 24
        return $this;
6892
    }
6893
6894
    /**
6895
     * @param array $array
6896
     *
6897
     * @return array
6898
     *
6899
     * @psalm-mutation-free
6900
     */
6901 25
    private function getArrayRecursiveHelperArrayy(array $array)
6902
    {
6903 25
        if ($array === []) {
6904
            return [];
6905
        }
6906
6907 25
        \array_walk_recursive(
6908 25
            $array,
6909
            /**
6910
             * @param array|self $item
6911
             *
6912
             * @return void
6913
             */
6914
            static function (&$item) {
6915 25
                if ($item instanceof self) {
6916 1
                    $item = $item->getArray();
6917
                }
6918 25
            }
6919
        );
6920
6921 25
        return $array;
6922
    }
6923
6924
    /**
6925
     * @param int|string|null $key
6926
     * @param mixed           $value
6927
     *
6928
     * @return void
6929
     */
6930 109
    private function checkType($key, $value)
6931
    {
6932
        if (
6933 109
            $key !== null
6934
            &&
6935 109
            isset($this->properties[$key]) === false
6936
            &&
6937 109
            $this->checkPropertiesMismatch === true
6938
        ) {
6939
            throw new \TypeError('The key ' . $key . ' does not exists in "properties". Maybe because @property was not used for the class (' . \get_class($this) . ').');
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'The key ' . $key . ' do...get_class($this) . ').'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
6940
        }
6941
6942 109
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
6943 96
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
6944 22
        } elseif ($key !== null && isset($this->properties[$key])) {
6945 22
            $this->properties[$key]->checkType($value);
6946
        }
6947 107
    }
6948
}
6949