Completed
Push — master ( d26e7d...811b94 )
by Lars
02:24
created

Arrayy   F

Complexity

Total Complexity 756

Size/Duplication

Total Lines 7921
Duplicated Lines 10.47 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 91.81%

Importance

Changes 0
Metric Value
dl 829
loc 7921
ccs 1962
cts 2137
cp 0.9181
rs 0.8
c 0
b 0
f 0
wmc 756
lcom 1
cbo 6

251 Methods

Rating   Name   Duplication   Size   Complexity  
A ksortImmutable() 0 11 1
A natcasesortImmutable() 0 11 1
A natsortImmutable() 0 11 1
A __construct() 0 17 1
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 14 2
A add() 0 18 5
A append() 0 24 5
A asort() 0 8 1
A asortImmutable() 0 11 1
A count() 0 12 3
A exchangeArray() 0 10 1
A getArrayCopy() 0 6 1
A getIteratorClass() 0 4 1
A ksort() 0 8 1
A natcasesort() 0 8 1
A natsort() 0 8 1
A offsetGet() 0 11 2
A serialize() 0 10 2
A setIteratorClass() 0 23 4
A appendImmutable() 25 27 4
A getIterator() 0 30 3
B offsetExists() 25 47 8
B offsetUnset() 28 51 8
A __clone() 0 10 3
A offsetSet() 0 18 3
A uasort() 0 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 23 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
B chunk() 21 50 8
A clean() 0 8 1
A clear() 0 19 4
B contains() 0 22 6
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 15 1
A countValues() 0 7 1
A create() 0 11 1
B flatten() 0 23 6
A createByReference() 0 7 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 35 5
A createFromTraversableImmutable() 0 4 1
A createWithRange() 0 7 1
A current() 0 8 2
A customSortKeys() 0 9 1
A customSortKeysImmutable() 0 14 1
A customSortValues() 0 8 1
A customSortValuesImmutable() 0 11 1
A delete() 0 8 2
A diff() 22 22 4
A diffKey() 22 22 4
A diffKeyAndValue() 0 28 5
B diffRecursive() 0 35 7
A diffReverse() 0 8 1
A divide() 0 11 1
A each() 15 15 2
A end() 0 19 5
A exists() 0 15 3
A fillWithDefaults() 0 23 3
B filter() 0 42 10
B filterBy() 0 72 3
A find() 10 10 3
A findBy() 0 4 1
A first() 0 9 2
A firstKey() 0 9 1
A firstsImmutable() 0 16 2
A firstsKeys() 16 16 2
A firstsMutable() 0 12 2
A flip() 0 14 2
F get() 6 173 40
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
C getColumn() 0 60 14
A getGeneratorByReference() 0 16 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() 17 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() 0 24 3
A isAssoc() 14 15 4
A isEmpty() 0 18 5
A isEqual() 0 4 1
A isMultiArray() 0 10 3
A isNumeric() 14 15 4
B isSequential() 0 27 8
A jsonSerialize() 0 4 1
A key() 0 8 2
A keyExists() 0 10 3
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 11 2
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 5 1
A mostUsedValues() 0 4 1
B moveElement() 0 33 6
A moveElementToFirstPlace() 16 16 2
A moveElementToLastPlace() 16 16 2
A next() 0 10 2
A nth() 0 19 3
A only() 18 18 3
A pad() 0 8 1
A partition() 0 16 3
A pop() 0 6 1
A prepend() 0 16 3
A prependImmutable() 24 24 4
A prependToEachKey() 17 26 4
A prependToEachValue() 28 28 5
A pull() 0 22 4
A push() 18 18 4
A randomImmutable() 6 31 3
A randomKey() 0 10 2
A randomKeys() 0 28 3
A randomMutable() 7 23 3
A randomValue() 0 10 2
A randomValues() 0 4 1
A randomWeighted() 0 15 4
A reduce() 15 15 2
A reduce_dimension() 0 23 5
A reindex() 0 8 1
A reject() 17 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 24 4
A repeat() 0 12 2
A replace() 0 10 1
A replaceAllKeys() 13 13 2
A replaceAllValues() 13 13 2
A replaceKeys() 14 14 2
A replaceOneValue() 0 15 2
A replaceValues() 0 9 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 42 8
A size() 0 4 1
A sizeIs() 0 16 3
A sizeIsBetween() 0 21 5
A sizeIsGreaterThan() 15 15 3
A sizeIsLessThan() 15 15 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
B split() 24 56 8
A stripEmpty() 0 12 2
A swap() 0 12 1
A toArray() 0 29 5
A toList() 0 8 1
A toJson() 0 9 2
A toPermutation() 0 36 4
A toString() 0 4 1
A uniqueNewIndex() 0 18 2
A uniqueKeepIndex() 0 26 2
A unique() 0 4 1
A unshift() 18 18 4
A validate() 10 10 3
A values() 0 12 2
A walk() 0 26 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
C getPropertiesFromPhpDoc() 32 55 12
B implode_recursive() 0 37 9
B in_array_recursive() 0 25 6
C internalGetArray() 0 51 12
B internalRemove() 0 33 7
C internalSet() 0 58 11
A objectToArray() 0 13 2
B setInitialValuesAndProperties() 0 45 10
A sorterKeys() 0 21 5
B sorting() 0 34 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
declare(strict_types=1);
4
5
namespace Arrayy;
6
7
use Arrayy\TypeCheck\TypeCheckArray;
8
use Arrayy\TypeCheck\TypeCheckInterface;
9
use Arrayy\TypeCheck\TypeCheckPhpDoc;
10
11
/**
12
 * Methods to manage arrays.
13
 *
14
 * For the full copyright and license information, please view the LICENSE
15
 * file that was distributed with this source code.
16
 *
17
 * @template TKey of array-key
18
 * @template T
19
 * @template-extends \ArrayObject<TKey,T>
20
 * @template-implements \IteratorAggregate<TKey,T>
21
 * @template-implements \ArrayAccess<TKey,T>
22
 */
23
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
24
{
25
    const ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES = '!!!!Arrayy_Helper_Types_For_All_Properties!!!!';
26
27
    const ARRAYY_HELPER_WALK = '!!!!Arrayy_Helper_Walk!!!!';
28
29
    /**
30
     * @var array
31
     *
32
     * @phpstan-var array<array-key|TKey,T>
33
     */
34
    protected $array = [];
35
36
    /**
37
     * @var \Arrayy\ArrayyRewindableGenerator|null
38
     *
39
     * @phpstan-var \Arrayy\ArrayyRewindableGenerator<TKey,T>|null
40
     */
41
    protected $generator;
42
43
    /**
44
     * @var string
45
     *
46
     * @phpstan-var class-string<\Arrayy\ArrayyIterator>
47
     */
48
    protected $iteratorClass = ArrayyIterator::class;
49
50
    /**
51
     * @var string
52
     */
53
    protected $pathSeparator = '.';
54
55
    /**
56
     * @var bool
57
     */
58
    protected $checkPropertyTypes = false;
59
60
    /**
61
     * @var bool
62
     */
63
    protected $checkForMissingPropertiesInConstructor = false;
64
65
    /**
66
     * @var bool
67
     */
68
    protected $checkPropertiesMismatchInConstructor = false;
69
70
    /**
71
     * @var bool
72
     */
73
    protected $checkPropertiesMismatch = true;
74
75
    /**
76
     * @var array<array-key,TypeCheckInterface>|TypeCheckArray<array-key,TypeCheckInterface>
77
     */
78
    protected $properties = [];
79
80
    /**
81
     * Initializes
82
     *
83
     * @param mixed  $data                         <p>
84
     *                                             Should be an array or a generator, otherwise it will try
85
     *                                             to convert it into an array.
86
     *                                             </p>
87
     * @param string $iteratorClass                optional <p>
88
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
89
     *                                             need this option.
90
     *                                             </p>
91
     * @param bool   $checkPropertiesInConstructor optional <p>
92
     *                                             You need to extend the "Arrayy"-class and you need to set
93
     *                                             the $checkPropertiesMismatchInConstructor class property
94
     *                                             to
95
     *                                             true, otherwise this option didn't not work anyway.
96
     *                                             </p>
97
     *
98
     * @phpstan-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
99
     */
100 1215
    public function __construct(
101
        $data = [],
102
        string $iteratorClass = ArrayyIterator::class,
103
        bool $checkPropertiesInConstructor = true
104
    ) {
105 1215
        $data = $this->fallbackForArray($data);
106
107
        // used only for serialize + unserialize, all other methods are overwritten
108
        /**
109
         * @psalm-suppress InvalidArgument - why?
110
         */
111 1213
        parent::__construct([], 0, $iteratorClass);
112
113 1213
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
114
115 1205
        $this->setIteratorClass($iteratorClass);
116 1205
    }
117
118
    /**
119
     * @return void
120
     */
121 53
    public function __clone()
122
    {
123 53
        if (!\is_array($this->properties)) {
124 1
            $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...
125
        }
126
127 53
        if ($this->generator !== null) {
128 1
            $this->generator = clone $this->generator;
129
        }
130 53
    }
131
132
    /**
133
     * Call object as function.
134
     *
135
     * @param mixed $key
136
     *
137
     * @return mixed
138
     *
139
     * @phpstan-param TKey $key
140
     * @phpstan-return false|T|array<TKey,T>
141
     */
142 1
    public function __invoke($key = null)
143
    {
144 1
        if ($key !== null) {
145 1
            $this->generatorToArray();
146
147 1
            return $this->array[$key] ?? false;
148
        }
149
150
        return $this->toArray();
151
    }
152
153
    /**
154
     * Whether or not an element exists by key.
155
     *
156
     * @param mixed $key
157
     *
158
     * @return bool
159
     *              <p>True is the key/index exists, otherwise false.</p>
160
     *
161
     * @phpstan-param TKey $key
162
     */
163
    public function __isset($key): bool
164
    {
165
        return $this->offsetExists($key);
166
    }
167
168
    /**
169
     * Assigns a value to the specified element.
170
     *
171
     * @param mixed $key
172
     * @param mixed $value
173
     *
174
     * @return void
175
     *
176
     * @phpstan-param TKey $key
177
     * @phpstan-param T $value
178
     */
179 3
    public function __set($key, $value)
180
    {
181 3
        $this->internalSet($key, $value);
182 3
    }
183
184
    /**
185
     * magic to string
186
     *
187
     * @return string
188
     */
189 15
    public function __toString(): string
190
    {
191 15
        return $this->toString();
192
    }
193
194
    /**
195
     * Unset element by key.
196
     *
197
     * @param mixed $key
198
     *
199
     * @phpstan-param TKey $key
200
     */
201
    public function __unset($key)
202
    {
203
        $this->internalRemove($key);
204
    }
205
206
    /**
207
     * Get a value by key.
208
     *
209
     * @param mixed $key
210
     *
211
     * @return mixed
212
     *               <p>Get a Value from the current array.</p>
213
     *
214
     * @phpstan-param TKey $key
215
     * @phpstan-return null|self<array-key,T>|T
216
     */
217 134
    public function &__get($key)
218
    {
219 134
        $return = $this->get($key, null, null, true);
220
221 134
        if (\is_array($return) === true) {
222
            $return = static::create(
223
                [],
224
                $this->iteratorClass,
225
                false
226
            )->createByReference($return);
227
        }
228
229 134
        return $return;
230
    }
231
232
    /**
233
     * Add new values (optional using dot-notation).
234
     *
235
     * @param mixed           $value
236
     * @param int|string|null $key
237
     *
238
     * @return static
239
     *                <p>(Immutable) Return this Arrayy object, with the appended values.</p>
240
     *
241
     * @phpstan-param T $value
242
     * @phpstan-param TKey $key
243
     * @phpstan-return static<TKey,T>
244
     *
245
     * @psalm-mutation-free
246
     */
247 13
    public function add($value, $key = null)
248
    {
249 13
        if ($key !== null) {
250 5
            $get = $this->get($key);
251 5
            if ($get !== null) {
252 1
                $value = \array_merge_recursive(
253 1
                    !$get instanceof self ? [$get] : $get->getArray(),
254 1
                    !\is_array($value) ? [$value] : $value
255
                );
256
            }
257
258 5
            $this->internalSet($key, $value);
259
260 4
            return $this;
261
        }
262
263 8
        return $this->append($value);
264
    }
265
266
    /**
267
     * Append a (key) + value to the current array.
268
     *
269
     * EXAMPLE: <code>
270
     * a(['fòô' => 'bàř'])->append('foo'); // Arrayy['fòô' => 'bàř', 0 => 'foo']
271
     * </code>
272
     *
273
     * @param mixed $value
274
     * @param mixed $key
275
     *
276
     * @return $this
277
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
278
     *
279
     * @phpstan-param T $value
280
     * @phpstan-param TKey|null $key
281
     * @phpstan-return static<TKey,T>
282
     */
283 20
    public function append($value, $key = null): self
284
    {
285 20
        $this->generatorToArray();
286
287 20
        if ($this->properties !== []) {
288 6
            $this->checkType($key, $value);
289
        }
290
291 19
        if ($key !== null) {
292
            if (
293 2
                isset($this->array[$key])
294
                &&
295 2
                \is_array($this->array[$key])
296
            ) {
297
                $this->array[$key][] = $value;
298
            } else {
299 2
                $this->array[$key] = $value;
300
            }
301
        } else {
302 17
            $this->array[] = $value;
303
        }
304
305 19
        return $this;
306
    }
307
308
    /**
309
     * Append a (key) + value to the current array.
310
     *
311
     * EXAMPLE: <code>
312
     * a(['fòô' => 'bàř'])->appendImmutable('foo')->getArray(); // ['fòô' => 'bàř', 0 => 'foo']
313
     * </code>
314
     *
315
     * @param mixed $value
316
     * @param mixed $key
317
     *
318
     * @return $this
319
     *               <p>(Immutable) Return this Arrayy object, with the appended values.</p>
320
     *
321
     * @phpstan-param T $value
322
     * @phpstan-param TKey $key
323
     * @phpstan-return static<TKey,T>
324
     * @psalm-mutation-free
325
     */
326 1 View Code Duplication
    public function appendImmutable($value, $key = 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...
327
    {
328
        /**
329
         * @phpstan-return \Generator<TKey,T> $generator
330
         */
331
        $generator = function () use ($key, $value): \Generator {
332 1
            if ($this->properties !== []) {
333
                $this->checkType($key, $value);
334
            }
335
336 1
            foreach ($this->getGenerator() as $keyOld => $itemOld) {
337 1
                yield $keyOld => $itemOld;
338
            }
339
340 1
            if ($key !== null) {
341
                yield $key => $value;
342
            } else {
343 1
                yield $value;
344
            }
345 1
        };
346
347 1
        return static::create(
348 1
            $generator,
349 1
            $this->iteratorClass,
350 1
            false
351
        );
352
    }
353
354
    /**
355
     * Sort the entries by value.
356
     *
357
     * @param int $sort_flags [optional] <p>
358
     *                        You may modify the behavior of the sort using the optional
359
     *                        parameter sort_flags, for details
360
     *                        see sort.
361
     *                        </p>
362
     *
363
     * @return $this
364
     *               <p>(Mutable) Return this Arrayy object.</p>
365
     *
366
     * @phpstan-return static<TKey,T>
367
     */
368 4
    public function asort(int $sort_flags = 0): self
369
    {
370 4
        $this->generatorToArray();
371
372 4
        \asort($this->array, $sort_flags);
373
374 4
        return $this;
375
    }
376
377
    /**
378
     * Sort the entries by value.
379
     *
380
     * @param int $sort_flags [optional] <p>
381
     *                        You may modify the behavior of the sort using the optional
382
     *                        parameter sort_flags, for details
383
     *                        see sort.
384
     *                        </p>
385
     *
386
     * @return $this
387
     *               <p>(Immutable) Return this Arrayy object.</p>
388
     *
389
     * @phpstan-return static<TKey,T>
390
     * @psalm-mutation-free
391
     */
392 4
    public function asortImmutable(int $sort_flags = 0): self
393
    {
394 4
        $that = clone $this;
395
396
        /**
397
         * @psalm-suppress ImpureMethodCall - object is already cloned
398
         */
399 4
        $that->asort($sort_flags);
400
401 4
        return $that;
402
    }
403
404
    /**
405
     * Counts all elements in an array, or something in an object.
406
     *
407
     * EXAMPLE: <code>
408
     * a([-9, -8, -7, 1.32])->count(); // 4
409
     * </code>
410
     *
411
     * <p>
412
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
413
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
414
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
415
     * implemented and used in PHP.
416
     * </p>
417
     *
418
     * @see http://php.net/manual/en/function.count.php
419
     *
420
     * @param int $mode [optional] If the optional mode parameter is set to
421
     *                  COUNT_RECURSIVE (or 1), count
422
     *                  will recursively count the array. This is particularly useful for
423
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
424
     *
425
     * @return int
426
     *             <p>
427
     *             The number of elements in var, which is
428
     *             typically an array, since anything else will have one
429
     *             element.
430
     *             </p>
431
     *             <p>
432
     *             If var is not an array or an object with
433
     *             implemented Countable interface,
434
     *             1 will be returned.
435
     *             There is one exception, if var is &null;,
436
     *             0 will be returned.
437
     *             </p>
438
     *             <p>
439
     *             Caution: count may return 0 for a variable that isn't set,
440
     *             but it may also return 0 for a variable that has been initialized with an
441
     *             empty array. Use isset to test if a variable is set.
442
     *             </p>
443
     * @psalm-mutation-free
444
     */
445 147
    public function count(int $mode = \COUNT_NORMAL): int
446
    {
447
        if (
448 147
            $this->generator
449
            &&
450 147
            $mode === \COUNT_NORMAL
451
        ) {
452 4
            return \iterator_count($this->generator);
453
        }
454
455 143
        return \count($this->toArray(), $mode);
456
    }
457
458
    /**
459
     * Exchange the array for another one.
460
     *
461
     * @param array|mixed|static $data
462
     *
463
     * 1. use the current array, if it's a array
464
     * 2. fallback to empty array, if there is nothing
465
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
466
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
467
     * 5. call "__toArray()" on object, if the method exists
468
     * 6. cast a string or object with "__toString()" into an array
469
     * 7. throw a "InvalidArgumentException"-Exception
470
     *
471
     * @return array
472
     *
473
     * @phpstan-param  T|array<TKey,T>|self<TKey,T> $data
474
     * @phpstan-return array<TKey,T>
475
     */
476 1
    public function exchangeArray($data): array
477
    {
478
        /** @phpstan-var array<TKey,T> array */
479 1
        $array = $this->fallbackForArray($data);
480
481 1
        $this->array = $array;
482 1
        $this->generator = null;
483
484 1
        return $this->array;
485
    }
486
487
    /**
488
     * Creates a copy of the ArrayyObject.
489
     *
490
     * @return array
491
     *
492
     * @phpstan-return array<int|string|TKey,T>
493
     */
494 6
    public function getArrayCopy(): array
495
    {
496 6
        $this->generatorToArray();
497
498 6
        return $this->array;
499
    }
500
501
    /**
502
     * Returns a new iterator, thus implementing the \Iterator interface.
503
     *
504
     * EXAMPLE: <code>
505
     * a(['foo', 'bar'])->getIterator(); // ArrayyIterator['foo', 'bar']
506
     * </code>
507
     *
508
     * @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...
509
     *                          <p>An iterator for the values in the array.</p>
510
     * @phpstan-return \Iterator<TKey, T>
511
     */
512 28
    public function getIterator(): \Iterator
513
    {
514 28
        if ($this->generator instanceof ArrayyRewindableGenerator) {
515 1
            $generator = clone $this->generator;
516
517
            /** @phpstan-var \Arrayy\ArrayyRewindableGenerator<TKey,T> */
518 1
            $generatorTmp = new ArrayyRewindableExtendedGenerator(
519
                static function () use ($generator): \Generator {
520 1
                    yield from $generator;
521 1
                },
522 1
                null,
523 1
                static::class
524
            );
525
526 1
            $this->generator = $generatorTmp;
527
528 1
            return $this->generator;
529
        }
530
531 27
        $iterator = $this->getIteratorClass();
532
533 27
        if ($iterator === ArrayyIterator::class) {
534 27
            return new $iterator($this->toArray(), 0, static::class);
535
        }
536
537
        $return = new $iterator($this->toArray());
538
        \assert($return instanceof \Iterator);
539
540
        return $return;
541
    }
542
543
    /**
544
     * Gets the iterator classname for the ArrayObject.
545
     *
546
     * @return string
547
     *
548
     * @phpstan-return class-string
549
     */
550 27
    public function getIteratorClass(): string
551
    {
552 27
        return $this->iteratorClass;
553
    }
554
555
    /**
556
     * Sort the entries by key.
557
     *
558
     * @param int $sort_flags [optional] <p>
559
     *                        You may modify the behavior of the sort using the optional
560
     *                        parameter sort_flags, for details
561
     *                        see sort.
562
     *                        </p>
563
     *
564
     * @return $this
565
     *               <p>(Mutable) Return this Arrayy object.</p>
566
     *
567
     * @phpstan-return static<TKey,T>
568
     */
569 4
    public function ksort(int $sort_flags = 0): self
570
    {
571 4
        $this->generatorToArray();
572
573 4
        \ksort($this->array, $sort_flags);
574
575 4
        return $this;
576
    }
577
578
    /**
579
     * Sort the entries by key.
580
     *
581
     * @param int $sort_flags [optional] <p>
582
     *                        You may modify the behavior of the sort using the optional
583
     *                        parameter sort_flags, for details
584
     *                        see sort.
585
     *                        </p>
586
     *
587
     * @return $this
588
     *               <p>(Immutable) Return this Arrayy object.</p>
589
     *
590
     * @phpstan-return static<TKey,T>
591
     */
592 4
    public function ksortImmutable(int $sort_flags = 0): self
593
    {
594 4
        $that = clone $this;
595
596
        /**
597
         * @psalm-suppress ImpureMethodCall - object is already cloned
598
         */
599 4
        $that->ksort($sort_flags);
600
601 4
        return $that;
602
    }
603
604
    /**
605
     * Sort an array using a case insensitive "natural order" algorithm.
606
     *
607
     * @return $this
608
     *               <p>(Mutable) Return this Arrayy object.</p>
609
     *
610
     * @phpstan-return static<TKey,T>
611
     */
612 8
    public function natcasesort(): self
613
    {
614 8
        $this->generatorToArray();
615
616 8
        \natcasesort($this->array);
617
618 8
        return $this;
619
    }
620
621
    /**
622
     * Sort an array using a case insensitive "natural order" algorithm.
623
     *
624
     * @return $this
625
     *               <p>(Immutable) Return this Arrayy object.</p>
626
     *
627
     * @phpstan-return static<TKey,T>
628
     * @psalm-mutation-free
629
     */
630 4
    public function natcasesortImmutable(): self
631
    {
632 4
        $that = clone $this;
633
634
        /**
635
         * @psalm-suppress ImpureMethodCall - object is already cloned
636
         */
637 4
        $that->natcasesort();
638
639 4
        return $that;
640
    }
641
642
    /**
643
     * Sort entries using a "natural order" algorithm.
644
     *
645
     * @return $this
646
     *               <p>(Mutable) Return this Arrayy object.</p>
647
     *
648
     * @phpstan-return static<TKey,T>
649
     */
650 10
    public function natsort(): self
651
    {
652 10
        $this->generatorToArray();
653
654 10
        \natsort($this->array);
655
656 10
        return $this;
657
    }
658
659
    /**
660
     * Sort entries using a "natural order" algorithm.
661
     *
662
     * @return $this
663
     *               <p>(Immutable) Return this Arrayy object.</p>
664
     *
665
     * @phpstan-return static<TKey,T>
666
     * @psalm-mutation-free
667
     */
668 4
    public function natsortImmutable(): self
669
    {
670 4
        $that = clone $this;
671
672
        /**
673
         * @psalm-suppress ImpureMethodCall - object is already cloned
674
         */
675 4
        $that->natsort();
676
677 4
        return $that;
678
    }
679
680
    /**
681
     * Whether or not an offset exists.
682
     *
683
     * @param bool|int|string $offset
684
     *
685
     * @return bool
686
     *
687
     * @psalm-mutation-free
688
     */
689 164
    public function offsetExists($offset): bool
690
    {
691
        // php cast "bool"-index into "int"-index
692 164
        if ((bool) $offset === $offset) {
693 1
            $offset = (int) $offset;
694
        }
695
        \assert(\is_int($offset) || \is_string($offset));
696
697 164
        $offsetExists = $this->keyExists($offset);
698 164
        if ($offsetExists === true) {
699 143
            return true;
700
        }
701
702
        /**
703
         * https://github.com/vimeo/psalm/issues/2536
704
         *
705
         * @psalm-suppress PossiblyInvalidArgument
706
         * @psalm-suppress InvalidScalarArgument
707
         */
708 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...
709 124
            $this->pathSeparator
710
            &&
711 124
            (string) $offset === $offset
712
            &&
713 124
            \strpos($offset, $this->pathSeparator) !== false
714
        ) {
715 4
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
716 4
            if ($explodedPath !== false) {
717
                /** @var string $lastOffset - helper for phpstan */
718 4
                $lastOffset = \array_pop($explodedPath);
719 4
                $containerPath = \implode($this->pathSeparator, $explodedPath);
720
721
                /**
722
                 * @psalm-suppress MissingClosureReturnType
723
                 * @psalm-suppress MissingClosureParamType
724
                 */
725 4
                $this->callAtPath(
726 4
                    $containerPath,
727
                    static function ($container) use ($lastOffset, &$offsetExists) {
728 4
                        $offsetExists = \array_key_exists($lastOffset, $container);
729 4
                    }
730
                );
731
            }
732
        }
733
734 124
        return $offsetExists;
735
    }
736
737
    /**
738
     * Returns the value at specified offset.
739
     *
740
     * @param int|string $offset
741
     *
742
     * @return mixed
743
     *               <p>Will return null if the offset did not exists.</p>
744
     *
745
     * @phpstan-param TKey $offset
746
     */
747 133
    public function &offsetGet($offset)
748
    {
749
        // init
750 133
        $value = null;
751
752 133
        if ($this->offsetExists($offset)) {
753 131
            $value = &$this->__get($offset);
754
        }
755
756 133
        return $value;
757
    }
758
759
    /**
760
     * Assigns a value to the specified offset + check the type.
761
     *
762
     * @param int|string|null $offset
763
     * @param mixed           $value
764
     *
765
     * @return void
766
     */
767 28
    public function offsetSet($offset, $value)
768
    {
769 28
        $this->generatorToArray();
770
771 28
        if ($offset === null) {
772 7
            if ($this->properties !== []) {
773 2
                $this->checkType(null, $value);
774
            }
775
776 6
            $this->array[] = $value;
777
        } else {
778 21
            $this->internalSet(
779 21
                $offset,
780 21
                $value,
781 21
                true
782
            );
783
        }
784 27
    }
785
786
    /**
787
     * Unset an offset.
788
     *
789
     * @param int|string $offset
790
     *
791
     * @return void
792
     *              <p>(Mutable) Return nothing.</p>
793
     */
794 26
    public function offsetUnset($offset)
795
    {
796 26
        $this->generatorToArray();
797
798 26
        if ($this->array === []) {
799 6
            return;
800
        }
801
802 21
        if ($this->keyExists($offset)) {
803 14
            unset($this->array[$offset]);
804
805 14
            return;
806
        }
807
808
        /**
809
         * https://github.com/vimeo/psalm/issues/2536
810
         *
811
         * @psalm-suppress PossiblyInvalidArgument
812
         * @psalm-suppress InvalidScalarArgument
813
         */
814 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...
815 10
            $this->pathSeparator
816
            &&
817 10
            (string) $offset === $offset
818
            &&
819 10
            \strpos($offset, $this->pathSeparator) !== false
820
        ) {
821 7
            $path = \explode($this->pathSeparator, (string) $offset);
822
823 7
            if ($path !== false) {
824 7
                $pathToUnset = \array_pop($path);
825
826
                /**
827
                 * @psalm-suppress MissingClosureReturnType
828
                 * @psalm-suppress MissingClosureParamType
829
                 */
830 7
                $this->callAtPath(
831 7
                    \implode($this->pathSeparator, $path),
832
                    static function (&$offset) use ($pathToUnset) {
833 6
                        if (\is_array($offset)) {
834 5
                            unset($offset[$pathToUnset]);
835
                        } else {
836 1
                            $offset = null;
837
                        }
838 7
                    }
839
                );
840
            }
841
        }
842
843 10
        unset($this->array[$offset]);
844 10
    }
845
846
    /**
847
     * Serialize the current "Arrayy"-object.
848
     *
849
     * EXAMPLE: <code>
850
     * a([1, 4, 7])->serialize();
851
     * </code>
852
     *
853
     * @return string
854
     */
855 2
    public function serialize(): string
856
    {
857 2
        $this->generatorToArray();
858
859 2
        if (\PHP_VERSION_ID < 70400) {
860 2
            return parent::serialize();
861
        }
862
863
        return \serialize($this);
864
    }
865
866
    /**
867
     * Sets the iterator classname for the current "Arrayy"-object.
868
     *
869
     * @param string $iteratorClass
870
     *
871
     * @throws \InvalidArgumentException
872
     *
873
     * @return void
874
     *
875
     * @phpstan-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
876
     */
877 1205
    public function setIteratorClass($iteratorClass)
878
    {
879 1205
        if (\class_exists($iteratorClass)) {
880 1205
            $this->iteratorClass = $iteratorClass;
881
882 1205
            return;
883
        }
884
885
        if (\strpos($iteratorClass, '\\') === 0) {
886
            /** @var class-string<\Arrayy\ArrayyIterator<TKey,T>> $iteratorClass */
0 ignored issues
show
Documentation introduced by
The doc-type class-string<\Arrayy\ArrayyIterator<TKey,T>> could not be parsed: Unknown type name "class-string" at position 0. (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...
887
            $iteratorClass = '\\' . $iteratorClass;
888
            if (\class_exists($iteratorClass)) {
889
                /**
890
                 * @psalm-suppress PropertyTypeCoercion
891
                 */
892
                $this->iteratorClass = $iteratorClass;
893
894
                return;
895
            }
896
        }
897
898
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
899
    }
900
901
    /**
902
     * Sort the entries with a user-defined comparison function and maintain key association.
903
     *
904
     * @param callable $callable
905
     *
906
     *@throws \InvalidArgumentException
907
     *
908
     * @return $this
909
     *               <p>(Mutable) Return this Arrayy object.</p>
910
     *
911
     * @phpstan-param  callable(T,T):int $callable
912
     * @phpstan-return static<TKey,T>
913
     */
914 8
    public function uasort($callable): self
915
    {
916 8
        if (!\is_callable($callable)) {
917
            throw new \InvalidArgumentException('Passed function must be callable');
918
        }
919
920 8
        $this->generatorToArray();
921
922 8
        \uasort($this->array, $callable);
923
924 8
        return $this;
925
    }
926
927
    /**
928
     * Sort the entries with a user-defined comparison function and maintain key association.
929
     *
930
     * @param callable $callable
931
     *
932
     *@throws \InvalidArgumentException
933
     *
934
     * @return $this
935
     *               <p>(Immutable) Return this Arrayy object.</p>
936
     *
937
     * @phpstan-param  callable(T,T):int $callable
938
     * @phpstan-return static<TKey,T>
939
     * @psalm-mutation-free
940
     */
941 4
    public function uasortImmutable($callable): self
942
    {
943 4
        $that = clone $this;
944
945
        /**
946
         * @psalm-suppress ImpureMethodCall - object is already cloned
947
         */
948 4
        $that->uasort($callable);
949
950 4
        return $that;
951
    }
952
953
    /**
954
     * Sort the entries by keys using a user-defined comparison function.
955
     *
956
     * @param callable $callable
957
     *
958
     * @throws \InvalidArgumentException
959
     *
960
     * @return static
961
     *                <p>(Mutable) Return this Arrayy object.</p>
962
     *
963
     * @phpstan-param  callable(TKey,TKey):int $callable
964
     * @phpstan-return static<TKey,T>
965
     */
966 5
    public function uksort($callable): self
967
    {
968 5
        return $this->customSortKeys($callable);
969
    }
970
971
    /**
972
     * Sort the entries by keys using a user-defined comparison function.
973
     *
974
     * @param callable $callable
975
     *
976
     * @throws \InvalidArgumentException
977
     *
978
     * @return static
979
     *                <p>(Immutable) Return this Arrayy object.</p>
980
     *
981
     * @phpstan-param  callable(TKey,TKey):int $callable
982
     * @phpstan-return static<TKey,T>
983
     * @psalm-mutation-free
984
     */
985 1
    public function uksortImmutable($callable): self
986
    {
987 1
        return $this->customSortKeysImmutable($callable);
988
    }
989
990
    /**
991
     * Unserialize an string and return the instance of the "Arrayy"-class.
992
     *
993
     * EXAMPLE: <code>
994
     * $serialized = a([1, 4, 7])->serialize();
995
     * a()->unserialize($serialized);
996
     * </code>
997
     *
998
     * @param string $string
999
     *
1000
     * @return $this
1001
     *
1002
     * @phpstan-return static<TKey,T>
1003
     */
1004 2
    public function unserialize($string): self
1005
    {
1006 2
        if (\PHP_VERSION_ID < 70400) {
1007 2
            parent::unserialize($string);
1008
1009 2
            return $this;
1010
        }
1011
1012
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
1013
    }
1014
1015
    /**
1016
     * Append a (key) + values to the current array.
1017
     *
1018
     * EXAMPLE: <code>
1019
     * a(['fòô' => ['bàř']])->appendArrayValues(['foo1', 'foo2'], 'fòô'); // Arrayy['fòô' => ['bàř', 'foo1', 'foo2']]
1020
     * </code>
1021
     *
1022
     * @param array $values
1023
     * @param mixed $key
1024
     *
1025
     * @return $this
1026
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
1027
     *
1028
     * @phpstan-param  array<array-key,T> $values
1029
     * @phpstan-param  TKey|null $key
1030
     * @phpstan-return static<TKey,T>
1031
     */
1032 1
    public function appendArrayValues(array $values, $key = null)
1033
    {
1034 1
        $this->generatorToArray();
1035
1036 1
        if ($key !== null) {
1037
            if (
1038 1
                isset($this->array[$key])
1039
                &&
1040 1
                \is_array($this->array[$key])
1041
            ) {
1042 1
                foreach ($values as $value) {
1043 1
                    $this->array[$key][] = $value;
1044
                }
1045
            } else {
1046 1
                foreach ($values as $value) {
1047
                    $this->array[$key] = $value;
1048
                }
1049
            }
1050
        } else {
1051
            foreach ($values as $value) {
1052
                $this->array[] = $value;
1053
            }
1054
        }
1055
1056 1
        return $this;
1057
    }
1058
1059
    /**
1060
     * Add a suffix to each key.
1061
     *
1062
     * @param int|string $prefix
1063
     *
1064
     * @return static
1065
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
1066
     *
1067
     * @phpstan-return static<TKey,T>
1068
     * @psalm-mutation-free
1069
     */
1070 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...
1071
    {
1072
        // init
1073 10
        $result = [];
1074
1075 10
        foreach ($this->getGenerator() as $key => $item) {
1076 9
            if ($item instanceof self) {
1077
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
1078 9
            } elseif (\is_array($item)) {
1079
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
1080
                    ->appendToEachKey($prefix)
1081
                    ->toArray();
1082
            } else {
1083 9
                $result[$prefix . $key] = $item;
1084
            }
1085
        }
1086
1087 10
        return self::create(
1088 10
            $result,
1089 10
            $this->iteratorClass,
1090 10
            false
1091
        );
1092
    }
1093
1094
    /**
1095
     * Add a prefix to each value.
1096
     *
1097
     * @param float|int|string $prefix
1098
     *
1099
     * @return static
1100
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
1101
     *
1102
     * @phpstan-return static<TKey,T>
1103
     * @psalm-mutation-free
1104
     */
1105 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...
1106
    {
1107
        // init
1108 10
        $result = [];
1109
1110 10
        foreach ($this->getGenerator() as $key => $item) {
1111 9
            if ($item instanceof self) {
1112
                $result[$key] = $item->appendToEachValue($prefix);
1113 9
            } elseif (\is_array($item)) {
1114
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
1115 9
            } elseif (\is_object($item) === true) {
1116 1
                $result[$key] = $item;
1117
            } else {
1118 8
                $result[$key] = $prefix . $item;
1119
            }
1120
        }
1121
1122 10
        return self::create($result, $this->iteratorClass, false);
1123
    }
1124
1125
    /**
1126
     * Sort an array in reverse order and maintain index association.
1127
     *
1128
     * @return $this
1129
     *               <p>(Mutable) Return this Arrayy object.</p>
1130
     *
1131
     * @phpstan-return static<TKey,T>
1132
     */
1133 4
    public function arsort(): self
1134
    {
1135 4
        $this->generatorToArray();
1136
1137 4
        \arsort($this->array);
1138
1139 4
        return $this;
1140
    }
1141
1142
    /**
1143
     * Sort an array in reverse order and maintain index association.
1144
     *
1145
     * @return $this
1146
     *               <p>(Immutable) Return this Arrayy object.</p>
1147
     *
1148
     * @phpstan-return static<TKey,T>
1149
     * @psalm-mutation-free
1150
     */
1151 10
    public function arsortImmutable(): self
1152
    {
1153 10
        $that = clone $this;
1154
1155 10
        $that->generatorToArray();
1156
1157 10
        \arsort($that->array);
1158
1159 10
        return $that;
1160
    }
1161
1162
    /**
1163
     * Iterate over the current array and execute a callback for each loop.
1164
     *
1165
     * EXAMPLE: <code>
1166
     * $result = A::create();
1167
     * $closure = function ($value, $key) use ($result) {
1168
     *     $result[$key] = ':' . $value . ':';
1169
     * };
1170
     * a(['foo', 'bar' => 'bis'])->at($closure); // Arrayy[':foo:', 'bar' => ':bis:']
1171
     * </code>
1172
     *
1173
     * @param \Closure $closure
1174
     *
1175
     * @return static
1176
     *                <p>(Immutable)</p>
1177
     *
1178
     * @phpstan-param \Closure(T=,TKey=):mixed $closure <p>INFO: \Closure result is not used, but void is not supported in PHP 7.0</p>
1179
     * @phpstan-return static<TKey,T>
1180
     * @psalm-mutation-free
1181
     */
1182 3
    public function at(\Closure $closure): self
1183
    {
1184 3
        $that = clone $this;
1185
1186 3
        foreach ($that->getGenerator() as $key => $value) {
1187 3
            $closure($value, $key);
1188
        }
1189
1190 3
        return static::create(
1191 3
            $that->toArray(),
1192 3
            $this->iteratorClass,
1193 3
            false
1194
        );
1195
    }
1196
1197
    /**
1198
     * Returns the average value of the current array.
1199
     *
1200
     * EXAMPLE: <code>
1201
     * a([-9, -8, -7, 1.32])->average(2); // -5.67
1202
     * </code>
1203
     *
1204
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
1205
     *
1206
     * @return float|int
1207
     *                   <p>The average value.</p>
1208
     * @psalm-mutation-free
1209
     */
1210 10
    public function average($decimals = 0)
1211
    {
1212 10
        $count = \count($this->toArray(), \COUNT_NORMAL);
1213
1214 10
        if (!$count) {
1215 2
            return 0;
1216
        }
1217
1218 8
        if ((int) $decimals !== $decimals) {
1219 3
            $decimals = 0;
1220
        }
1221
1222 8
        return \round(\array_sum($this->toArray()) / $count, $decimals);
1223
    }
1224
1225
    /**
1226
     * Changes all keys in an array.
1227
     *
1228
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
1229
     *                  or <strong>CASE_LOWER</strong> (default)</p>
1230
     *
1231
     * @return static
1232
     *                <p>(Immutable)</p>
1233
     *
1234
     * @phpstan-return static<TKey,T>
1235
     * @psalm-mutation-free
1236
     */
1237 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
1238
    {
1239
        if (
1240 1
            $case !== \CASE_LOWER
1241
            &&
1242 1
            $case !== \CASE_UPPER
1243
        ) {
1244
            $case = \CASE_LOWER;
1245
        }
1246
1247 1
        $return = [];
1248 1
        foreach ($this->getGenerator() as $key => $value) {
1249
            \assert(\is_string($key) || \is_int($key) || \is_float($key));
1250
1251 1
            if ($case === \CASE_LOWER) {
1252 1
                $key = \mb_strtolower((string) $key);
1253
            } else {
1254 1
                $key = \mb_strtoupper((string) $key);
1255
            }
1256
1257 1
            $return[$key] = $value;
1258
        }
1259
1260 1
        return static::create(
1261 1
            $return,
1262 1
            $this->iteratorClass,
1263 1
            false
1264
        );
1265
    }
1266
1267
    /**
1268
     * Change the path separator of the array wrapper.
1269
     *
1270
     * By default, the separator is: "."
1271
     *
1272
     * @param string $separator <p>Separator to set.</p>
1273
     *
1274
     * @return $this
1275
     *               <p>(Mutable) Return this Arrayy object.</p>
1276
     *
1277
     * @phpstan-return static<TKey,T>
1278
     */
1279 11
    public function changeSeparator($separator): self
1280
    {
1281 11
        $this->pathSeparator = $separator;
1282
1283 11
        return $this;
1284
    }
1285
1286
    /**
1287
     * Create a chunked version of the current array.
1288
     *
1289
     * EXAMPLE: <code>
1290
     * a([-9, -8, -7, 1.32])->chunk(2); // Arrayy[[-9, -8], [-7, 1.32]]
1291
     * </code>
1292
     *
1293
     * @param int  $size         <p>Size of each chunk.</p>
1294
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
1295
     *
1296
     * @return static
1297
     *                <p>(Immutable) A new array of chunks from the original array.</p>
1298
     *
1299
     * @phpstan-return static<TKey,T>
1300
     * @psalm-mutation-free
1301
     */
1302 5
    public function chunk($size, $preserveKeys = false): self
1303
    {
1304 5
        if ($preserveKeys) {
1305
            $generator = function () use ($size) {
1306
                $values = [];
1307
                $tmpCounter = 0;
1308
                foreach ($this->getGenerator() as $key => $value) {
1309
                    ++$tmpCounter;
1310
1311
                    $values[$key] = $value;
1312
                    if ($tmpCounter === $size) {
1313
                        yield $values;
1314
1315
                        $values = [];
1316
                        $tmpCounter = 0;
1317
                    }
1318
                }
1319
1320
                if ($values !== []) {
1321
                    yield $values;
1322
                }
1323
            };
1324 View Code Duplication
        } else {
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...
1325
            $generator = function () use ($size) {
1326 5
                $values = [];
1327 5
                $tmpCounter = 0;
1328 5
                foreach ($this->getGenerator() as $value) {
1329 5
                    ++$tmpCounter;
1330
1331 5
                    $values[] = $value;
1332 5
                    if ($tmpCounter === $size) {
1333 5
                        yield $values;
1334
1335 5
                        $values = [];
1336 5
                        $tmpCounter = 0;
1337
                    }
1338
                }
1339
1340 5
                if ($values !== []) {
1341 4
                    yield $values;
1342
                }
1343 5
            };
1344
        }
1345
1346 5
        return static::create(
1347 5
            $generator,
1348 5
            $this->iteratorClass,
1349 5
            false
1350
        );
1351
    }
1352
1353
    /**
1354
     * Clean all falsy values from the current array.
1355
     *
1356
     * EXAMPLE: <code>
1357
     * a([-8 => -9, 1, 2 => false])->clean(); // Arrayy[-8 => -9, 1]
1358
     * </code>
1359
     *
1360
     * @return static
1361
     *                <p>(Immutable)</p>
1362
     *
1363
     * @phpstan-return static<TKey,T>
1364
     * @psalm-mutation-free
1365
     */
1366 8
    public function clean(): self
1367
    {
1368 8
        return $this->filter(
1369
            static function ($value) {
1370 7
                return (bool) $value;
1371 8
            }
1372
        );
1373
    }
1374
1375
    /**
1376
     * WARNING!!! -> Clear the current full array or a $key of it.
1377
     *
1378
     * EXAMPLE: <code>
1379
     * a([-8 => -9, 1, 2 => false])->clear(); // Arrayy[]
1380
     * </code>
1381
     *
1382
     * @param int|int[]|string|string[]|null $key
1383
     *
1384
     * @return $this
1385
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1386
     *
1387
     * @phpstan-return static<TKey,T>
1388
     */
1389 10
    public function clear($key = null): self
1390
    {
1391 10
        if ($key !== null) {
1392 3
            if (\is_array($key)) {
1393 1
                foreach ($key as $keyTmp) {
1394 1
                    $this->offsetUnset($keyTmp);
1395
                }
1396
            } else {
1397 2
                $this->offsetUnset($key);
1398
            }
1399
1400 3
            return $this;
1401
        }
1402
1403 7
        $this->array = [];
1404 7
        $this->generator = null;
1405
1406 7
        return $this;
1407
    }
1408
1409
    /**
1410
     * Check if an item is in the current array.
1411
     *
1412
     * EXAMPLE: <code>
1413
     * a([1, true])->contains(true); // true
1414
     * </code>
1415
     *
1416
     * @param float|int|string $value
1417
     * @param bool             $recursive
1418
     * @param bool             $strict
1419
     *
1420
     * @return bool
1421
     * @psalm-mutation-free
1422
     */
1423 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1424
    {
1425 23
        if ($recursive === true) {
1426 18
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1427
        }
1428
1429
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1430 14
        foreach ($this->getGeneratorByReference() as &$valueFromArray) {
1431 11
            if ($strict) {
1432 11
                if ($value === $valueFromArray) {
1433 11
                    return true;
1434
                }
1435
            } else {
1436
                /** @noinspection NestedPositiveIfStatementsInspection */
1437
                if ($value == $valueFromArray) {
1438
                    return true;
1439
                }
1440
            }
1441
        }
1442
1443 7
        return false;
1444
    }
1445
1446
    /**
1447
     * Check if an (case-insensitive) string is in the current array.
1448
     *
1449
     * EXAMPLE: <code>
1450
     * a(['E', 'é'])->containsCaseInsensitive('É'); // true
1451
     * </code>
1452
     *
1453
     * @param mixed $value
1454
     * @param bool  $recursive
1455
     *
1456
     * @return bool
1457
     * @psalm-mutation-free
1458
     *
1459
     * @psalm-suppress InvalidCast - hack for int|float|bool support
1460
     */
1461 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1462
    {
1463 26
        if ($value === null) {
1464 2
            return false;
1465
        }
1466
1467 24
        if ($recursive === true) {
1468
            /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1469 24
            foreach ($this->getGeneratorByReference() as &$valueTmp) {
1470 22
                if (\is_array($valueTmp)) {
1471 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1472 5
                    if ($return === true) {
1473 5
                        return $return;
1474
                    }
1475 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1476 16
                    return true;
1477
                }
1478
            }
1479
1480 8
            return false;
1481
        }
1482
1483
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1484 12
        foreach ($this->getGeneratorByReference() as &$valueTmp) {
1485 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1486 8
                return true;
1487
            }
1488
        }
1489
1490 4
        return false;
1491
    }
1492
1493
    /**
1494
     * Check if the given key/index exists in the array.
1495
     *
1496
     * EXAMPLE: <code>
1497
     * a([1 => true])->containsKey(1); // true
1498
     * </code>
1499
     *
1500
     * @param int|string $key <p>key/index to search for</p>
1501
     *
1502
     * @return bool
1503
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1504
     *
1505
     * @psalm-mutation-free
1506
     */
1507 4
    public function containsKey($key): bool
1508
    {
1509 4
        return $this->offsetExists($key);
1510
    }
1511
1512
    /**
1513
     * Check if all given needles are present in the array as key/index.
1514
     *
1515
     * EXAMPLE: <code>
1516
     * a([1 => true])->containsKeys(array(1 => 0)); // true
1517
     * </code>
1518
     *
1519
     * @param array $needles   <p>The keys you are searching for.</p>
1520
     * @param bool  $recursive
1521
     *
1522
     * @return bool
1523
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1524
     *
1525
     * @phpstan-param array<array-key>|array<TKey> $needles
1526
     * @psalm-mutation-free
1527
     */
1528 2
    public function containsKeys(array $needles, $recursive = false): bool
1529
    {
1530 2
        if ($recursive === true) {
1531
            return
1532 2
                \count(
1533 2
                    \array_intersect(
1534 2
                        $needles,
1535 2
                        $this->keys(true)->toArray()
1536
                    ),
1537 2
                    \COUNT_RECURSIVE
1538
                )
1539
                ===
1540 2
                \count(
1541 2
                    $needles,
1542 2
                    \COUNT_RECURSIVE
1543
                );
1544
        }
1545
1546 1
        return \count(
1547 1
            \array_intersect($needles, $this->keys()->toArray()),
1548 1
            \COUNT_NORMAL
1549
        )
1550
                ===
1551 1
                \count(
1552 1
                    $needles,
1553 1
                    \COUNT_NORMAL
1554
                );
1555
    }
1556
1557
    /**
1558
     * Check if all given needles are present in the array as key/index.
1559
     *
1560
     * @param array $needles <p>The keys you are searching for.</p>
1561
     *
1562
     * @return bool
1563
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1564
     *
1565
     * @phpstan-param array<array-key>|array<TKey> $needles
1566
     * @psalm-mutation-free
1567
     */
1568 1
    public function containsKeysRecursive(array $needles): bool
1569
    {
1570 1
        return $this->containsKeys($needles, true);
1571
    }
1572
1573
    /**
1574
     * alias: for "Arrayy->contains()"
1575
     *
1576
     * @param float|int|string $value
1577
     *
1578
     * @return bool
1579
     *
1580
     * @see Arrayy::contains()
1581
     * @psalm-mutation-free
1582
     */
1583 9
    public function containsValue($value): bool
1584
    {
1585 9
        return $this->contains($value);
1586
    }
1587
1588
    /**
1589
     * alias: for "Arrayy->contains($value, true)"
1590
     *
1591
     * @param float|int|string $value
1592
     *
1593
     * @return bool
1594
     *
1595
     * @see Arrayy::contains()
1596
     * @psalm-mutation-free
1597
     */
1598 18
    public function containsValueRecursive($value): bool
1599
    {
1600 18
        return $this->contains($value, true);
1601
    }
1602
1603
    /**
1604
     * Check if all given needles are present in the array.
1605
     *
1606
     * EXAMPLE: <code>
1607
     * a([1, true])->containsValues(array(1, true)); // true
1608
     * </code>
1609
     *
1610
     * @param array $needles
1611
     *
1612
     * @return bool
1613
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1614
     *
1615
     * @phpstan-param array<mixed>|array<T> $needles
1616
     * @psalm-mutation-free
1617
     */
1618 1
    public function containsValues(array $needles): bool
1619
    {
1620 1
        return \count(
1621 1
            \array_intersect(
1622 1
                $needles,
1623 1
                $this->toArray()
1624
            ),
1625 1
            \COUNT_NORMAL
1626
        )
1627
               ===
1628 1
               \count(
1629 1
                   $needles,
1630 1
                   \COUNT_NORMAL
1631
               );
1632
    }
1633
1634
    /**
1635
     * Counts all the values of an array
1636
     *
1637
     * @see          http://php.net/manual/en/function.array-count-values.php
1638
     *
1639
     * @return static
1640
     *                <p>
1641
     *                (Immutable)
1642
     *                An associative Arrayy-object of values from input as
1643
     *                keys and their count as value.
1644
     *                </p>
1645
     *
1646
     * @phpstan-return static<array-key,int>
1647
     * @psalm-mutation-free
1648
     */
1649 7
    public function countValues(): self
1650
    {
1651
        /** @phpstan-var static<array-key,int> $return - help for phpstan */
1652 7
        $return = self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1653
1654 7
        return $return;
1655
    }
1656
1657
    /**
1658
     * Creates an Arrayy object.
1659
     *
1660
     * @param mixed  $data
1661
     * @param string $iteratorClass
1662
     * @param bool   $checkPropertiesInConstructor
1663
     *
1664
     * @return static
1665
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1666
     *
1667
     * @phpstan-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1668
     * @phpstan-return static<TKey,T>
1669
     * @psalm-mutation-free
1670
     */
1671 732
    public static function create(
1672
        $data = [],
1673
        string $iteratorClass = ArrayyIterator::class,
1674
        bool $checkPropertiesInConstructor = true
1675
    ) {
1676 732
        return new static(
1677 732
            $data,
1678 732
            $iteratorClass,
1679 732
            $checkPropertiesInConstructor
1680
        );
1681
    }
1682
1683
    /**
1684
     * Flatten an array with the given character as a key delimiter.
1685
     *
1686
     * EXAMPLE: <code>
1687
     * $dot = a(['foo' => ['abc' => 'xyz', 'bar' => ['baz']]]);
1688
     * $flatten = $dot->flatten();
1689
     * $flatten['foo.abc']; // 'xyz'
1690
     * $flatten['foo.bar.0']; // 'baz'
1691
     * </code>
1692
     *
1693
     * @param string     $delimiter
1694
     * @param string     $prepend
1695
     * @param array|null $items
1696
     *
1697
     * @return array
1698
     */
1699 2
    public function flatten($delimiter = '.', $prepend = '', $items = null)
1700
    {
1701
        // init
1702 2
        $flatten = [];
1703
1704 2
        if ($items === null) {
1705 2
            $items = $this->getArray();
1706
        }
1707
1708 2
        foreach ($items as $key => $value) {
1709 2
            if (\is_array($value) && $value !== []) {
1710 2
                $flatten[] = $this->flatten($delimiter, $prepend . $key . $delimiter, $value);
1711
            } else {
1712 2
                $flatten[] = [$prepend . $key => $value];
1713
            }
1714
        }
1715
1716 2
        if (\count($flatten) === 0) {
1717
            return [];
1718
        }
1719
1720 2
        return \array_merge_recursive([], ...$flatten);
1721
    }
1722
1723
    /**
1724
     * WARNING: Creates an Arrayy object by reference.
1725
     *
1726
     * @param array $array
1727
     *
1728
     * @return $this
1729
     *               <p>(Mutable) Return this Arrayy object.</p>
1730
     *
1731
     * @phpstan-param  array<TKey,T> $array
1732
     * @phpstan-return $this<TKey,T>
1733
     *
1734
     * @internal this will not check any types because it's set directly as reference
1735
     */
1736 26
    public function createByReference(array &$array = []): self
1737
    {
1738 26
        $this->array = &$array;
1739 26
        $this->generator = null;
1740
1741 26
        return $this;
1742
    }
1743
1744
    /**
1745
     * Create an new instance from a callable function which will return an Generator.
1746
     *
1747
     * @param callable $generatorFunction
1748
     *
1749
     * @return static
1750
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1751
     *
1752
     * @phpstan-param callable():\Generator<TKey,T> $generatorFunction
1753
     * @phpstan-return static<TKey,T>
1754
     * @psalm-mutation-free
1755
     */
1756 8
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1757
    {
1758 8
        return self::create($generatorFunction);
1759
    }
1760
1761
    /**
1762
     * Create an new instance filled with a copy of values from a "Generator"-object.
1763
     *
1764
     * @param \Generator $generator
1765
     *
1766
     * @return static
1767
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1768
     *
1769
     * @phpstan-param \Generator<TKey,T> $generator
1770
     * @phpstan-return static<TKey,T>
1771
     * @psalm-mutation-free
1772
     */
1773 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1774
    {
1775 4
        return self::create(\iterator_to_array($generator, true));
1776
    }
1777
1778
    /**
1779
     * Create an new Arrayy object via JSON.
1780
     *
1781
     * @param string $json
1782
     *
1783
     * @return static
1784
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1785
     *
1786
     * @phpstan-return static<int|string,mixed>
1787
     * @psalm-mutation-free
1788
     */
1789 6
    public static function createFromJson(string $json): self
1790
    {
1791 6
        return static::create(\json_decode($json, true));
1792
    }
1793
1794
    /**
1795
     * Create an new Arrayy object via JSON.
1796
     *
1797
     * @param array $array
1798
     *
1799
     * @return static
1800
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1801
     *
1802
     * @phpstan-param array<TKey,T> $array
1803
     * @phpstan-return static<TKey,T>
1804
     * @psalm-mutation-free
1805
     */
1806 1
    public static function createFromArray(array $array): self
1807
    {
1808 1
        return static::create($array);
1809
    }
1810
1811
    /**
1812
     * Create an new instance filled with values from an object that is iterable.
1813
     *
1814
     * @param \Traversable $object <p>iterable object</p>
1815
     *
1816
     * @return static
1817
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1818
     *
1819
     * @phpstan-param \Traversable<array-key,T> $object
1820
     * @phpstan-return static<array-key,T>
1821
     * @psalm-mutation-free
1822
     */
1823 4
    public static function createFromObject(\Traversable $object): self
1824
    {
1825
        // init
1826 4
        $arrayy = new static();
1827
1828 4
        if ($object instanceof self) {
1829 4
            $objectArray = $object->getGenerator();
1830
        } else {
1831
            $objectArray = $object;
1832
        }
1833
1834 4
        foreach ($objectArray as $key => $value) {
1835
            /**
1836
             * @psalm-suppress ImpureMethodCall - object is already re-created
1837
             */
1838 3
            $arrayy->internalSet($key, $value);
1839
        }
1840
1841 4
        return $arrayy;
1842
    }
1843
1844
    /**
1845
     * Create an new instance filled with values from an object.
1846
     *
1847
     * @param object $object
1848
     *
1849
     * @return static
1850
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1851
     *
1852
     * @phpstan-return static<array-key,mixed>
1853
     * @psalm-mutation-free
1854
     */
1855 5
    public static function createFromObjectVars($object): self
1856
    {
1857 5
        return self::create(self::objectToArray($object));
1858
    }
1859
1860
    /**
1861
     * Create an new Arrayy object via string.
1862
     *
1863
     * @param string      $str       <p>The input string.</p>
1864
     * @param string|null $delimiter <p>The boundary string.</p>
1865
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1866
     *                               used.</p>
1867
     *
1868
     * @return static
1869
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1870
     *
1871
     * @phpstan-return static<int,string>
1872
     * @psalm-mutation-free
1873
     */
1874 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1875
    {
1876 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...
1877 1
            \preg_match_all($regEx, $str, $array);
1878
1879 1
            if (!empty($array)) {
1880 1
                $array = $array[0];
1881
            }
1882
        } else {
1883
            /** @noinspection NestedPositiveIfStatementsInspection */
1884 9
            if ($delimiter !== null) {
1885 7
                $array = \explode($delimiter, $str);
1886
            } else {
1887 2
                $array = [$str];
1888
            }
1889
        }
1890
1891
        // trim all string in the array
1892
        /**
1893
         * @psalm-suppress MissingClosureParamType
1894
         */
1895 10
        \array_walk(
1896 10
            $array,
1897
            static function (&$val) {
1898 10
                if ((string) $val === $val) {
1899 10
                    $val = \trim($val);
1900
                }
1901 10
            }
1902
        );
1903
1904
        /** @var static<int,string> $return - help for phpstan */
0 ignored issues
show
Documentation introduced by
The doc-type static<int,string> 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...
1905 10
        $return = static::create($array);
1906
1907 10
        return $return;
1908
    }
1909
1910
    /**
1911
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1912
     *
1913
     * @param \Traversable $traversable
1914
     * @param bool         $use_keys    [optional] <p>
1915
     *                                  Whether to use the iterator element keys as index.
1916
     *                                  </p>
1917
     *
1918
     * @return static
1919
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1920
     *
1921
     * @phpstan-param \Traversable<array-key|TKey,T> $traversable
1922
     * @phpstan-return static<int|TKey,T>
1923
     * @psalm-mutation-free
1924
     */
1925 1
    public static function createFromTraversableImmutable(\Traversable $traversable, bool $use_keys = true): self
1926
    {
1927 1
        return self::create(\iterator_to_array($traversable, $use_keys));
1928
    }
1929
1930
    /**
1931
     * Create an new instance containing a range of elements.
1932
     *
1933
     * @param float|int|string $low  <p>First value of the sequence.</p>
1934
     * @param float|int|string $high <p>The sequence is ended upon reaching the end value.</p>
1935
     * @param float|int        $step <p>Used as the increment between elements in the sequence.</p>
1936
     *
1937
     * @return static
1938
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1939
     *
1940
     * @phpstan-return static<int,int|string>
1941
     * @psalm-mutation-free
1942
     */
1943 2
    public static function createWithRange($low, $high, $step = 1): self
1944
    {
1945
        /** @phpstan-var static<int,int|string> $return - help for phpstan */
1946 2
        $return = static::create(\range($low, $high, $step));
1947
1948 2
        return $return;
1949
    }
1950
1951
    /**
1952
     * Gets the element of the array at the current internal iterator position.
1953
     *
1954
     * @return false|mixed
1955
     *
1956
     * @phpstan-return false|T
1957
     */
1958
    public function current()
1959
    {
1960
        if ($this->generator) {
1961
            return $this->generator->current();
1962
        }
1963
1964
        return \current($this->array);
1965
    }
1966
1967
    /**
1968
     * Custom sort by index via "uksort".
1969
     *
1970
     * EXAMPLE: <code>
1971
     * $callable = function ($a, $b) {
1972
     *     if ($a == $b) {
1973
     *         return 0;
1974
     *     }
1975
     *     return ($a > $b) ? 1 : -1;
1976
     * };
1977
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1978
     * $resultArrayy = $arrayy->customSortKeys($callable); // Arrayy['one' => 1, 'three' => 3, 'two' => 2]
1979
     * </code>
1980
     *
1981
     * @see          http://php.net/manual/en/function.uksort.php
1982
     *
1983
     * @param callable $callable
1984
     *
1985
     * @throws \InvalidArgumentException
1986
     *
1987
     * @return $this
1988
     *               <p>(Mutable) Return this Arrayy object.</p>
1989
     *
1990
     * @phpstan-param  callable(TKey,TKey):int $callable
1991
     * @phpstan-return static<TKey,T>
1992
     */
1993 5
    public function customSortKeys(callable $callable): self
1994
    {
1995 5
        $this->generatorToArray();
1996
1997
        /* @phpstan-ignore-next-line | false-positive for "callable((int|string), (int|string)): int" vs. "callable(TKey of (int|string), TKey of (int|string)): int" */
1998 5
        \uksort($this->array, $callable);
1999
2000 5
        return $this;
2001
    }
2002
2003
    /**
2004
     * Custom sort by index via "uksort".
2005
     *
2006
     * @see          http://php.net/manual/en/function.uksort.php
2007
     *
2008
     * @param callable $callable
2009
     *
2010
     * @throws \InvalidArgumentException
2011
     *
2012
     * @return $this
2013
     *               <p>(Immutable) Return this Arrayy object.</p>
2014
     *
2015
     * @phpstan-param  callable(TKey,TKey):int $callable
2016
     * @phpstan-return static<TKey,T>
2017
     * @psalm-mutation-free
2018
     */
2019 1
    public function customSortKeysImmutable(callable $callable): self
2020
    {
2021 1
        $that = clone $this;
2022
2023 1
        $that->generatorToArray();
2024
2025
        /**
2026
         * @psalm-suppress ImpureFunctionCall - object is already cloned
2027
         * @phpstan-ignore-next-line | false-positive for "callable((int|string), (int|string)): int" vs. "callable(TKey of (int|string), TKey of (int|string)): int"
2028
         */
2029 1
        \uksort($that->array, $callable);
2030
2031 1
        return $that;
2032
    }
2033
2034
    /**
2035
     * Custom sort by value via "usort".
2036
     *
2037
     * EXAMPLE: <code>
2038
     * $callable = function ($a, $b) {
2039
     *     if ($a == $b) {
2040
     *         return 0;
2041
     *     }
2042
     *     return ($a > $b) ? 1 : -1;
2043
     * };
2044
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
2045
     * $resultArrayy = $arrayy->customSortValues($callable); // Arrayy[1, 2, 3]
2046
     * </code>
2047
     *
2048
     * @see          http://php.net/manual/en/function.usort.php
2049
     *
2050
     * @param callable $callable
2051
     *
2052
     * @return $this
2053
     *               <p>(Mutable) Return this Arrayy object.</p>
2054
     *
2055
     * @phpstan-param  callable(T,T):int $callable
2056
     * @phpstan-return static<int,T>
2057
     */
2058 11
    public function customSortValues(callable $callable): self
2059
    {
2060 11
        $this->generatorToArray();
2061
2062 11
        \usort($this->array, $callable);
2063
2064 11
        return $this;
2065
    }
2066
2067
    /**
2068
     * Custom sort by value via "usort".
2069
     *
2070
     * @see          http://php.net/manual/en/function.usort.php
2071
     *
2072
     * @param callable $callable
2073
     *
2074
     * @throws \InvalidArgumentException
2075
     *
2076
     * @return $this
2077
     *               <p>(Immutable) Return this Arrayy object.</p>
2078
     *
2079
     * @phpstan-param  callable(T,T):int $callable
2080
     * @phpstan-return static<int,T>
2081
     * @psalm-mutation-free
2082
     */
2083 5
    public function customSortValuesImmutable($callable): self
2084
    {
2085 5
        $that = clone $this;
2086
2087
        /**
2088
         * @psalm-suppress ImpureMethodCall - object is already cloned
2089
         */
2090 5
        $that->customSortValues($callable);
2091
2092 5
        return $that;
2093
    }
2094
2095
    /**
2096
     * Delete the given key or keys.
2097
     *
2098
     * @param int|int[]|string|string[] $keyOrKeys
2099
     *
2100
     * @return void
2101
     */
2102 9
    public function delete($keyOrKeys)
2103
    {
2104 9
        $keyOrKeys = (array) $keyOrKeys;
2105
2106 9
        foreach ($keyOrKeys as $key) {
2107 9
            $this->offsetUnset($key);
2108
        }
2109 9
    }
2110
2111
    /**
2112
     * Return elements where the values that are only in the current array.
2113
     *
2114
     * EXAMPLE: <code>
2115
     * a([1 => 1, 2 => 2])->diff([1 => 1]); // Arrayy[2 => 2]
2116
     * </code>
2117
     *
2118
     * @param array ...$array
2119
     *
2120
     * @return static
2121
     *                <p>(Immutable)</p>
2122
     *
2123
     * @phpstan-param  array<TKey,T> ...$array
2124
     * @phpstan-return static<TKey,T>
2125
     * @psalm-mutation-free
2126
     */
2127 13 View Code Duplication
    public function diff(array ...$array): 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...
2128
    {
2129 13
        if (\count($array) > 1) {
2130 1
            $array = \array_merge([], ...$array);
2131
        } else {
2132 13
            $array = $array[0];
2133
        }
2134
2135
        $generator = function () use ($array): \Generator {
2136 13
            foreach ($this->getGenerator() as $key => $value) {
2137 11
                if (\in_array($value, $array, true) === false) {
2138 5
                    yield $key => $value;
2139
                }
2140
            }
2141 13
        };
2142
2143 13
        return static::create(
2144 13
            $generator,
2145 13
            $this->iteratorClass,
2146 13
            false
2147
        );
2148
    }
2149
2150
    /**
2151
     * Return elements where the keys are only in the current array.
2152
     *
2153
     * @param array ...$array
2154
     *
2155
     * @return static
2156
     *                <p>(Immutable)</p>
2157
     *
2158
     * @phpstan-param  array<TKey,T> ...$array
2159
     * @phpstan-return static<TKey,T>
2160
     * @psalm-mutation-free
2161
     */
2162 9 View Code Duplication
    public function diffKey(array ...$array): 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...
2163
    {
2164 9
        if (\count($array) > 1) {
2165 1
            $array = \array_replace([], ...$array);
2166
        } else {
2167 8
            $array = $array[0];
2168
        }
2169
2170
        $generator = function () use ($array): \Generator {
2171 9
            foreach ($this->getGenerator() as $key => $value) {
2172 8
                if (\array_key_exists($key, $array) === false) {
2173 2
                    yield $key => $value;
2174
                }
2175
            }
2176 9
        };
2177
2178 9
        return static::create(
2179 9
            $generator,
2180 9
            $this->iteratorClass,
2181 9
            false
2182
        );
2183
    }
2184
2185
    /**
2186
     * Return elements where the values and keys are only in the current array.
2187
     *
2188
     * @param array ...$array
2189
     *
2190
     * @return static
2191
     *                <p>(Immutable)</p>
2192
     *
2193
     * @phpstan-param  array<TKey,T> $array
2194
     * @phpstan-return static<TKey,T>
2195
     * @psalm-mutation-free
2196
     */
2197 9
    public function diffKeyAndValue(array ...$array): self
2198
    {
2199 9
        if (\count($array) > 1) {
2200 1
            $array = \array_merge([], ...$array);
2201
        } else {
2202 8
            $array = $array[0];
2203
        }
2204
2205
        $generator = function () use ($array): \Generator {
2206 9
            foreach ($this->getGenerator() as $key => $value) {
2207 8
                $isset = isset($array[$key]);
2208
2209
                if (
2210 8
                    !$isset
2211
                    ||
2212 8
                    $array[$key] !== $value
2213
                ) {
2214 4
                    yield $key => $value;
2215
                }
2216
            }
2217 9
        };
2218
2219 9
        return static::create(
2220 9
            $generator,
2221 9
            $this->iteratorClass,
2222 9
            false
2223
        );
2224
    }
2225
2226
    /**
2227
     * Return elements where the values are only in the current multi-dimensional array.
2228
     *
2229
     * EXAMPLE: <code>
2230
     * a([1 => [1 => 1], 2 => [2 => 2]])->diffRecursive([1 => [1 => 1]]); // Arrayy[2 => [2 => 2]]
2231
     * </code>
2232
     *
2233
     * @param array                 $array
2234
     * @param array|\Generator|null $helperVariableForRecursion <p>(only for internal usage)</p>
2235
     *
2236
     * @return static
2237
     *                <p>(Immutable)</p>
2238
     *
2239
     * @phpstan-param  array<TKey,T> $array
2240
     * @phpstan-param  null|array<TKey,T>|\Generator<TKey,T> $helperVariableForRecursion
2241
     * @phpstan-return static<TKey,T>
2242
     * @psalm-mutation-free
2243
     */
2244 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
2245
    {
2246
        // init
2247 1
        $result = [];
2248
2249
        if (
2250 1
            $helperVariableForRecursion !== null
2251
            &&
2252 1
            \is_array($helperVariableForRecursion)
2253
        ) {
2254
            $arrayForTheLoop = $helperVariableForRecursion;
2255
        } else {
2256 1
            $arrayForTheLoop = $this->getGenerator();
2257
        }
2258
2259 1
        foreach ($arrayForTheLoop as $key => $value) {
2260 1
            if ($value instanceof self) {
2261 1
                $value = $value->toArray();
2262
            }
2263
2264 1
            if (\array_key_exists($key, $array)) {
2265 1
                if ($value !== $array[$key]) {
2266 1
                    $result[$key] = $value;
2267
                }
2268
            } else {
2269 1
                $result[$key] = $value;
2270
            }
2271
        }
2272
2273 1
        return static::create(
2274 1
            $result,
2275 1
            $this->iteratorClass,
2276 1
            false
2277
        );
2278
    }
2279
2280
    /**
2281
     * Return elements where the values that are only in the new $array.
2282
     *
2283
     * EXAMPLE: <code>
2284
     * a([1 => 1])->diffReverse([1 => 1, 2 => 2]); // Arrayy[2 => 2]
2285
     * </code>
2286
     *
2287
     * @param array $array
2288
     *
2289
     * @return static
2290
     *                <p>(Immutable)</p>
2291
     *
2292
     * @phpstan-param  array<TKey,T> $array
2293
     * @phpstan-return static<TKey,T>
2294
     * @psalm-mutation-free
2295
     */
2296 8
    public function diffReverse(array $array = []): self
2297
    {
2298 8
        return static::create(
2299 8
            \array_diff($array, $this->toArray()),
2300 8
            $this->iteratorClass,
2301 8
            false
2302
        );
2303
    }
2304
2305
    /**
2306
     * Divide an array into two arrays. One with keys and the other with values.
2307
     *
2308
     * EXAMPLE: <code>
2309
     * a(['a' => 1, 'b' => ''])->divide(); // Arrayy[Arrayy['a', 'b'], Arrayy[1, '']]
2310
     * </code>
2311
     *
2312
     * @return static
2313
     *                <p>(Immutable)</p>
2314
     *
2315
     * @phpstan-return static<TKey,T>
2316
     * @psalm-mutation-free
2317
     */
2318 1
    public function divide(): self
2319
    {
2320 1
        return static::create(
2321
            [
2322 1
                $this->keys(),
2323 1
                $this->values(),
2324
            ],
2325 1
            $this->iteratorClass,
2326 1
            false
2327
        );
2328
    }
2329
2330
    /**
2331
     * Iterate over the current array and modify the array's value.
2332
     *
2333
     * EXAMPLE: <code>
2334
     * $result = A::create();
2335
     * $closure = function ($value) {
2336
     *     return ':' . $value . ':';
2337
     * };
2338
     * a(['foo', 'bar' => 'bis'])->each($closure); // Arrayy[':foo:', 'bar' => ':bis:']
2339
     * </code>
2340
     *
2341
     * @param \Closure $closure
2342
     *
2343
     * @return static
2344
     *                <p>(Immutable)</p>
2345
     *
2346
     * @phpstan-param \Closure(T=,?TKey=):T $closure
2347
     * @phpstan-return static<TKey,T>
2348
     * @psalm-mutation-free
2349
     */
2350 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...
2351
    {
2352
        // init
2353 5
        $array = [];
2354
2355 5
        foreach ($this->getGenerator() as $key => $value) {
2356 5
            $array[$key] = $closure($value, $key);
2357
        }
2358
2359 5
        return static::create(
2360 5
            $array,
2361 5
            $this->iteratorClass,
2362 5
            false
2363
        );
2364
    }
2365
2366
    /**
2367
     * Sets the internal iterator to the last element in the array and returns this element.
2368
     *
2369
     * @return false|mixed
2370
     *
2371
     * @phpstan-return T|false
2372
     */
2373
    public function end()
2374
    {
2375
        if ($this->generator) {
2376
            $count = $this->count();
2377
            if ($count === 0) {
2378
                return false;
2379
            }
2380
2381
            $counter = 0;
2382
            /** @noinspection PhpUnusedLocalVariableInspection */
2383
            foreach ($this->getIterator() as $item) {
2384
                if (++$counter === $count - 1) {
2385
                    break;
2386
                }
2387
            }
2388
        }
2389
2390
        return \end($this->array);
2391
    }
2392
2393
    /**
2394
     * Check if a value is in the current array using a closure.
2395
     *
2396
     * EXAMPLE: <code>
2397
     * $callable = function ($value, $key) {
2398
     *     return 2 === $key and 'two' === $value;
2399
     * };
2400
     * a(['foo', 2 => 'two'])->exists($callable); // true
2401
     * </code>
2402
     *
2403
     * @param \Closure $closure
2404
     *
2405
     * @return bool
2406
     *              <p>Returns true if the given value is found, false otherwise.</p>
2407
     *
2408
     * @phpstan-param \Closure(T=,TKey=):bool $closure
2409
     */
2410 4
    public function exists(\Closure $closure): bool
2411
    {
2412
        // init
2413 4
        $isExists = false;
2414
2415 4
        foreach ($this->getGenerator() as $key => $value) {
2416 3
            if ($closure($value, $key)) {
2417 1
                $isExists = true;
2418
2419 1
                break;
2420
            }
2421
        }
2422
2423 4
        return $isExists;
2424
    }
2425
2426
    /**
2427
     * Fill the array until "$num" with "$default" values.
2428
     *
2429
     * EXAMPLE: <code>
2430
     * a(['bar'])->fillWithDefaults(3, 'foo'); // Arrayy['bar', 'foo', 'foo']
2431
     * </code>
2432
     *
2433
     * @param int   $num
2434
     * @param mixed $default
2435
     *
2436
     * @return static
2437
     *                <p>(Immutable)</p>
2438
     *
2439
     * @phpstan-param T $default
2440
     * @phpstan-return static<TKey,T>
2441
     * @psalm-mutation-free
2442
     */
2443 8
    public function fillWithDefaults(int $num, $default = null): self
2444
    {
2445 8
        if ($num < 0) {
2446 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
2447
        }
2448
2449 7
        $this->generatorToArray();
2450
2451 7
        $tmpArray = $this->array;
2452
2453 7
        $count = \count($tmpArray);
2454
2455 7
        while ($count < $num) {
2456 4
            $tmpArray[] = $default;
2457 4
            ++$count;
2458
        }
2459
2460 7
        return static::create(
2461 7
            $tmpArray,
2462 7
            $this->iteratorClass,
2463 7
            false
2464
        );
2465
    }
2466
2467
    /**
2468
     * Find all items in an array that pass the truth test.
2469
     *
2470
     * EXAMPLE: <code>
2471
     * $closure = function ($value) {
2472
     *     return $value % 2 !== 0;
2473
     * }
2474
     * a([1, 2, 3, 4])->filter($closure); // Arrayy[0 => 1, 2 => 3]
2475
     * </code>
2476
     *
2477
     * @param \Closure|null $closure [optional] <p>
2478
     *                               The callback function to use
2479
     *                               </p>
2480
     *                               <p>
2481
     *                               If no callback is supplied, all entries of
2482
     *                               input equal to false (see
2483
     *                               converting to
2484
     *                               boolean) will be removed.
2485
     *                               </p>
2486
     * @param int           $flag    [optional] <p>
2487
     *                               Flag determining what arguments are sent to <i>callback</i>:
2488
     *                               </p>
2489
     *                               <ul>
2490
     *                               <li>
2491
     *                               <b>ARRAY_FILTER_USE_KEY</b> (1) - pass key as the only argument
2492
     *                               to <i>callback</i> instead of the value
2493
     *                               </li>
2494
     *                               <li>
2495
     *                               <b>ARRAY_FILTER_USE_BOTH</b> (2) - pass both value and key as
2496
     *                               arguments to <i>callback</i> instead of the value
2497
     *                               </li>
2498
     *                               </ul>
2499
     *
2500
     * @return static
2501
     *                <p>(Immutable)</p>
2502
     *
2503
     * @phpstan-param null|(\Closure(T=,TKey=):bool)|(\Closure(T=):bool)|(\Closure(TKey=):bool) $closure
2504
     * @phpstan-return static<TKey,T>
2505
     * @psalm-mutation-free
2506
     */
2507 13
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
2508
    {
2509 13
        if (!$closure) {
2510 1
            return $this->clean();
2511
        }
2512
2513 13
        if ($flag === \ARRAY_FILTER_USE_KEY) {
2514
            $generator = function () use ($closure) {
2515 1
                foreach ($this->getGenerator() as $key => $value) {
2516 1
                    if ($closure($key) === true) {
2517 1
                        yield $key => $value;
2518
                    }
2519
                }
2520 1
            };
2521 13
        } elseif ($flag === \ARRAY_FILTER_USE_BOTH) {
2522
            /** @noinspection PhpSillyAssignmentInspection - hack for phpstan - https://github.com/phpstan/phpstan/issues/4192 */
2523
            /** @phpstan-var \Closure(T=,TKey=):bool $closure */
2524 13
            $closure = $closure;
0 ignored issues
show
Bug introduced by
Why assign $closure to itself?

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

This assignement can be removed without consequences.

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

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

This assignement can be removed without consequences.

Loading history...
6145
6146
                $array[$key] = $this->shuffle($secure, $value);
6147
            }
6148
        }
6149
6150 2
        return static::create(
6151 2
            $array,
6152 2
            $this->iteratorClass,
6153 2
            false
6154
        );
6155
    }
6156
6157
    /**
6158
     * Count the values from the current array.
6159
     *
6160
     * alias: for "Arrayy->count()"
6161
     *
6162
     * @param int $mode
6163
     *
6164
     * @return int
6165
     */
6166 20
    public function size(int $mode = \COUNT_NORMAL): int
6167
    {
6168 20
        return $this->count($mode);
6169
    }
6170
6171
    /**
6172
     * Checks whether array has exactly $size items.
6173
     *
6174
     * @param int $size
6175
     *
6176
     * @return bool
6177
     */
6178 1
    public function sizeIs(int $size): bool
6179
    {
6180
        // init
6181 1
        $itemsTempCount = 0;
6182
6183
        /** @noinspection PhpUnusedLocalVariableInspection */
6184
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
6185 1
        foreach ($this->getGeneratorByReference() as &$value) {
6186 1
            ++$itemsTempCount;
6187 1
            if ($itemsTempCount > $size) {
6188 1
                return false;
6189
            }
6190
        }
6191
6192 1
        return $itemsTempCount === $size;
6193
    }
6194
6195
    /**
6196
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
6197
     * smaller than $fromSize.
6198
     *
6199
     * @param int $fromSize
6200
     * @param int $toSize
6201
     *
6202
     * @return bool
6203
     */
6204 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
6205
    {
6206 1
        if ($fromSize > $toSize) {
6207 1
            $tmp = $toSize;
6208 1
            $toSize = $fromSize;
6209 1
            $fromSize = $tmp;
6210
        }
6211
6212
        // init
6213 1
        $itemsTempCount = 0;
6214
6215
        /** @noinspection PhpUnusedLocalVariableInspection */
6216 1
        foreach ($this->getGenerator() as $value) {
6217 1
            ++$itemsTempCount;
6218 1
            if ($itemsTempCount > $toSize) {
6219 1
                return false;
6220
            }
6221
        }
6222
6223 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
6224
    }
6225
6226
    /**
6227
     * Checks whether array has more than $size items.
6228
     *
6229
     * @param int $size
6230
     *
6231
     * @return bool
6232
     */
6233 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...
6234
    {
6235
        // init
6236 1
        $itemsTempCount = 0;
6237
6238
        /** @noinspection PhpUnusedLocalVariableInspection */
6239 1
        foreach ($this->getGenerator() as $value) {
6240 1
            ++$itemsTempCount;
6241 1
            if ($itemsTempCount > $size) {
6242 1
                return true;
6243
            }
6244
        }
6245
6246 1
        return $itemsTempCount > $size;
6247
    }
6248
6249
    /**
6250
     * Checks whether array has less than $size items.
6251
     *
6252
     * @param int $size
6253
     *
6254
     * @return bool
6255
     */
6256 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...
6257
    {
6258
        // init
6259 1
        $itemsTempCount = 0;
6260
6261
        /** @noinspection PhpUnusedLocalVariableInspection */
6262 1
        foreach ($this->getGenerator() as $value) {
6263 1
            ++$itemsTempCount;
6264 1
            if ($itemsTempCount > $size) {
6265 1
                return false;
6266
            }
6267
        }
6268
6269 1
        return $itemsTempCount < $size;
6270
    }
6271
6272
    /**
6273
     * Counts all elements in an array, or something in an object.
6274
     *
6275
     * <p>
6276
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
6277
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
6278
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
6279
     * implemented and used in PHP.
6280
     * </p>
6281
     *
6282
     * @return int
6283
     *             <p>
6284
     *             The number of elements in var, which is
6285
     *             typically an array, since anything else will have one
6286
     *             element.
6287
     *             </p>
6288
     *             <p>
6289
     *             If var is not an array or an object with
6290
     *             implemented Countable interface,
6291
     *             1 will be returned.
6292
     *             There is one exception, if var is &null;,
6293
     *             0 will be returned.
6294
     *             </p>
6295
     *             <p>
6296
     *             Caution: count may return 0 for a variable that isn't set,
6297
     *             but it may also return 0 for a variable that has been initialized with an
6298
     *             empty array. Use isset to test if a variable is set.
6299
     *             </p>
6300
     */
6301 10
    public function sizeRecursive(): int
6302
    {
6303 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
6304
    }
6305
6306
    /**
6307
     * Extract a slice of the array.
6308
     *
6309
     * @param int      $offset       <p>Slice begin index.</p>
6310
     * @param int|null $length       <p>Length of the slice.</p>
6311
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
6312
     *
6313
     * @return static
6314
     *                <p>(Immutable) A slice of the original array with length $length.</p>
6315
     *
6316
     * @phpstan-return static<TKey,T>
6317
     * @psalm-mutation-free
6318
     */
6319 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
6320
    {
6321 5
        return static::create(
6322 5
            \array_slice(
6323 5
                $this->toArray(),
6324 5
                $offset,
6325 5
                $length,
6326 5
                $preserveKeys
6327
            ),
6328 5
            $this->iteratorClass,
6329 5
            false
6330
        );
6331
    }
6332
6333
    /**
6334
     * Sort the current array and optional you can keep the keys.
6335
     *
6336
     * EXAMPLE: <code>
6337
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sort(SORT_ASC, SORT_NATURAL, false); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
6338
     * </code>
6339
     *
6340
     * @param int|string $direction
6341
     *                              <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6342
     * @param int        $strategy
6343
     *                              <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
6344
     *                              <strong>SORT_NATURAL</strong></p>
6345
     * @param bool       $keepKeys
6346
     *
6347
     * @return static
6348
     *                <p>(Mutable) Return this Arrayy object.</p>
6349
     *
6350
     * @phpstan-return static<int|TKey,T>
6351
     */
6352 20
    public function sort(
6353
        $direction = \SORT_ASC,
6354
        int $strategy = \SORT_REGULAR,
6355
        bool $keepKeys = false
6356
    ): self {
6357 20
        $this->generatorToArray();
6358
6359 20
        return $this->sorting(
6360 20
            $this->array,
6361 20
            $direction,
6362 20
            $strategy,
6363 20
            $keepKeys
6364
        );
6365
    }
6366
6367
    /**
6368
     * Sort the current array and optional you can keep the keys.
6369
     *
6370
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6371
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
6372
     *                              <strong>SORT_NATURAL</strong></p>
6373
     * @param bool       $keepKeys
6374
     *
6375
     * @return static
6376
     *                <p>(Immutable) Return this Arrayy object.</p>
6377
     *
6378
     * @phpstan-return static<int|TKey,T>
6379
     */
6380 12
    public function sortImmutable(
6381
        $direction = \SORT_ASC,
6382
        int $strategy = \SORT_REGULAR,
6383
        bool $keepKeys = false
6384
    ): self {
6385 12
        $that = clone $this;
6386
6387 12
        $that->generatorToArray();
6388
6389 12
        return $that->sorting(
6390 12
            $that->array,
6391 12
            $direction,
6392 12
            $strategy,
6393 12
            $keepKeys
6394
        );
6395
    }
6396
6397
    /**
6398
     * Sort the current array by key.
6399
     *
6400
     * EXAMPLE: <code>
6401
     * a([1 => 2, 0 => 1])->sortKeys(\SORT_ASC); // Arrayy[0 => 1, 1 => 2]
6402
     * </code>
6403
     *
6404
     * @see http://php.net/manual/en/function.ksort.php
6405
     * @see http://php.net/manual/en/function.krsort.php
6406
     *
6407
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6408
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6409
     *                              <strong>SORT_NATURAL</strong></p>
6410
     *
6411
     * @return $this
6412
     *               <p>(Mutable) Return this Arrayy object.</p>
6413
     *
6414
     * @phpstan-return static<TKey,T>
6415
     */
6416 18
    public function sortKeys(
6417
        $direction = \SORT_ASC,
6418
        int $strategy = \SORT_REGULAR
6419
    ): self {
6420 18
        $this->generatorToArray();
6421
6422 18
        $this->sorterKeys($this->array, $direction, $strategy);
6423
6424 18
        return $this;
6425
    }
6426
6427
    /**
6428
     * Sort the current array by key.
6429
     *
6430
     * @see          http://php.net/manual/en/function.ksort.php
6431
     * @see          http://php.net/manual/en/function.krsort.php
6432
     *
6433
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6434
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6435
     *                              <strong>SORT_NATURAL</strong></p>
6436
     *
6437
     * @return $this
6438
     *               <p>(Immutable) Return this Arrayy object.</p>
6439
     *
6440
     * @phpstan-return static<TKey,T>
6441
     * @psalm-mutation-free
6442
     */
6443 8
    public function sortKeysImmutable(
6444
        $direction = \SORT_ASC,
6445
        int $strategy = \SORT_REGULAR
6446
    ): self {
6447 8
        $that = clone $this;
6448
6449
        /**
6450
         * @psalm-suppress ImpureMethodCall - object is already cloned
6451
         */
6452 8
        $that->sortKeys($direction, $strategy);
6453
6454 8
        return $that;
6455
    }
6456
6457
    /**
6458
     * Sort the current array by value.
6459
     *
6460
     * EXAMPLE: <code>
6461
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueKeepIndex(SORT_ASC, SORT_REGULAR); // Arrayy[0 => 'a', 3 => 'd', 2 => 'f']
6462
     * </code>
6463
     *
6464
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6465
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6466
     *                              <strong>SORT_NATURAL</strong></p>
6467
     *
6468
     * @return static
6469
     *                <p>(Mutable)</p>
6470
     *
6471
     * @phpstan-return static<int|TKey,T>
6472
     */
6473 1
    public function sortValueKeepIndex(
6474
        $direction = \SORT_ASC,
6475
        int $strategy = \SORT_REGULAR
6476
    ): self {
6477 1
        return $this->sort($direction, $strategy, true);
6478
    }
6479
6480
    /**
6481
     * Sort the current array by value.
6482
     *
6483
     * EXAMPLE: <code>
6484
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueNewIndex(SORT_ASC, SORT_NATURAL); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
6485
     * </code>
6486
     *
6487
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6488
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6489
     *                              <strong>SORT_NATURAL</strong></p>
6490
     *
6491
     * @return static
6492
     *                <p>(Mutable)</p>
6493
     *
6494
     * @phpstan-return static<int|TKey,T>
6495
     */
6496 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6497
    {
6498 1
        return $this->sort($direction, $strategy, false);
6499
    }
6500
6501
    /**
6502
     * Sort a array by value or by a closure.
6503
     *
6504
     * - If the sorter is null, the array is sorted naturally.
6505
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
6506
     *
6507
     * EXAMPLE: <code>
6508
     * $testArray = range(1, 5);
6509
     * $under = a($testArray)->sorter(
6510
     *     function ($value) {
6511
     *         return $value % 2 === 0;
6512
     *     }
6513
     * );
6514
     * var_dump($under); // Arrayy[1, 3, 5, 2, 4]
6515
     * </code>
6516
     *
6517
     * @param callable|mixed|null $sorter
6518
     * @param int|string          $direction <p>use <strong>SORT_ASC</strong> (default) or
6519
     *                                       <strong>SORT_DESC</strong></p>
6520
     * @param int                 $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6521
     *                                       <strong>SORT_NATURAL</strong></p>
6522
     *
6523
     * @return static
6524
     *                <p>(Immutable)</p>
6525
     *
6526
     * @pslam-param callable|T|null $sorter
6527
     * @phpstan-return static<TKey,T>
6528
     * @psalm-mutation-free
6529
     */
6530 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6531
    {
6532 1
        $array = $this->toArray();
6533 1
        $direction = $this->getDirection($direction);
6534
6535
        // Transform all values into their results.
6536 1
        if ($sorter) {
6537 1
            $arrayy = static::create(
6538 1
                $array,
6539 1
                $this->iteratorClass,
6540 1
                false
6541
            );
6542
6543
            /**
6544
             * @psalm-suppress MissingClosureReturnType
6545
             * @psalm-suppress MissingClosureParamType
6546
             */
6547 1
            $results = $arrayy->each(
6548
                static function ($value) use ($sorter) {
6549 1
                    if (\is_callable($sorter) === true) {
6550 1
                        return $sorter($value);
6551
                    }
6552
6553 1
                    return $sorter === $value;
6554 1
                }
6555
            );
6556
6557 1
            $results = $results->toArray();
6558
        } else {
6559 1
            $results = $array;
6560
        }
6561
6562
        // Sort by the results and replace by original values
6563 1
        \array_multisort($results, $direction, $strategy, $array);
6564
6565 1
        return static::create(
6566 1
            $array,
6567 1
            $this->iteratorClass,
6568 1
            false
6569
        );
6570
    }
6571
6572
    /**
6573
     * @param int      $offset
6574
     * @param int|null $length
6575
     * @param array    $replacement
6576
     *
6577
     * @return static
6578
     *                <p>(Immutable)</p>
6579
     *
6580
     * @phpstan-param  array<array-key,T> $replacement
6581
     * @phpstan-return static<TKey,T>
6582
     * @psalm-mutation-free
6583
     */
6584 1
    public function splice(int $offset, int $length = null, $replacement = []): self
6585
    {
6586 1
        $tmpArray = $this->toArray();
6587
6588 1
        \array_splice(
6589 1
            $tmpArray,
6590 1
            $offset,
6591 1
            $length ?? $this->count(),
6592 1
            $replacement
6593
        );
6594
6595 1
        return static::create(
6596 1
            $tmpArray,
6597 1
            $this->iteratorClass,
6598 1
            false
6599
        );
6600
    }
6601
6602
    /**
6603
     * Split an array in the given amount of pieces.
6604
     *
6605
     * EXAMPLE: <code>
6606
     * a(['a' => 1, 'b' => 2])->split(2, true); // Arrayy[['a' => 1], ['b' => 2]]
6607
     * </code>
6608
     *
6609
     * @param int  $numberOfPieces
6610
     * @param bool $keepKeys
6611
     *
6612
     * @return static
6613
     *                <p>(Immutable)</p>
6614
     *
6615
     * @phpstan-return static<TKey,T>
6616
     * @psalm-mutation-free
6617
     */
6618 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
6619
    {
6620 1
        if ($keepKeys) {
6621
            $generator = function () use ($numberOfPieces) {
6622 1
                $carry = [];
6623 1
                $i = 1;
6624 1
                foreach ($this->getGenerator() as $key => $value) {
6625 1
                    $carry[$key] = $value;
6626
6627 1
                    if ($i % $numberOfPieces !== 0) {
6628 1
                        ++$i;
6629
6630 1
                        continue;
6631
                    }
6632
6633 1
                    yield $carry;
6634
6635 1
                    $carry = [];
6636 1
                    $i = 1;
6637
                }
6638
6639 1
                if ($carry !== []) {
6640 1
                    yield $carry;
6641
                }
6642 1
            };
6643 View Code Duplication
        } else {
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...
6644
            $generator = function () use ($numberOfPieces) {
6645 1
                $carry = [];
6646 1
                $i = 1;
6647 1
                foreach ($this->getGenerator() as $value) {
6648 1
                    $carry[] = $value;
6649
6650 1
                    if ($i % $numberOfPieces !== 0) {
6651 1
                        ++$i;
6652
6653 1
                        continue;
6654
                    }
6655
6656 1
                    yield $carry;
6657
6658 1
                    $carry = [];
6659 1
                    $i = 1;
6660
                }
6661
6662 1
                if ($carry !== []) {
6663 1
                    yield $carry;
6664
                }
6665 1
            };
6666
        }
6667
6668 1
        return static::create(
6669 1
            $generator,
6670 1
            $this->iteratorClass,
6671 1
            false
6672
        );
6673
    }
6674
6675
    /**
6676
     * Strip all empty items from the current array.
6677
     *
6678
     * EXAMPLE: <code>
6679
     * a(['a' => 1, 'b' => ''])->stripEmpty(); // Arrayy[['a' => 1]]
6680
     * </code>
6681
     *
6682
     * @return static
6683
     *                <p>(Immutable)</p>
6684
     *
6685
     * @phpstan-return static<TKey,T>
6686
     * @psalm-mutation-free
6687
     */
6688 1
    public function stripEmpty(): self
6689
    {
6690 1
        return $this->filter(
6691
            static function ($item) {
6692 1
                if ($item === null) {
6693 1
                    return false;
6694
                }
6695
6696 1
                return (bool) \trim((string) $item);
6697 1
            }
6698
        );
6699
    }
6700
6701
    /**
6702
     * Swap two values between positions by key.
6703
     *
6704
     * EXAMPLE: <code>
6705
     * a(['a' => 1, 'b' => ''])->swap('a', 'b'); // Arrayy[['a' => '', 'b' => 1]]
6706
     * </code>
6707
     *
6708
     * @param int|string $swapA <p>a key in the array</p>
6709
     * @param int|string $swapB <p>a key in the array</p>
6710
     *
6711
     * @return static
6712
     *                <p>(Immutable)</p>
6713
     *
6714
     * @phpstan-return static<TKey,T>
6715
     * @psalm-mutation-free
6716
     */
6717 1
    public function swap($swapA, $swapB): self
6718
    {
6719 1
        $array = $this->toArray();
6720
6721 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
6722
6723 1
        return static::create(
6724 1
            $array,
6725 1
            $this->iteratorClass,
6726 1
            false
6727
        );
6728
    }
6729
6730
    /**
6731
     * Get the current array from the "Arrayy"-object.
6732
     * alias for "getArray()"
6733
     *
6734
     * @param bool $convertAllArrayyElements <p>
6735
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6736
     *                                       </p>
6737
     * @param bool $preserveKeys             <p>
6738
     *                                       e.g.: A generator maybe return the same key more then once,
6739
     *                                       so maybe you will ignore the keys.
6740
     *                                       </p>
6741
     *
6742
     * @return array
6743
     *
6744
     * @phpstan-return array<TKey,T>
6745
     * @psalm-mutation-free
6746
     */
6747 944
    public function toArray(
6748
        bool $convertAllArrayyElements = false,
6749
        bool $preserveKeys = true
6750
    ): array {
6751
        // init
6752 944
        $array = [];
6753
6754 944
        if ($convertAllArrayyElements) {
6755 3
            foreach ($this->getGenerator() as $key => $value) {
6756 3
                if ($value instanceof self) {
6757 2
                    $value = $value->toArray(
6758 2
                        $convertAllArrayyElements,
6759 2
                        $preserveKeys
6760
                    );
6761
                }
6762
6763 3
                if ($preserveKeys) {
6764 2
                    $array[$key] = $value;
6765
                } else {
6766 1
                    $array[] = $value;
6767
                }
6768
            }
6769
        } else {
6770 943
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
6771
        }
6772
6773
        /** @phpstan-ignore-next-line - depends on the $convertAllArrayyElements parameter :/ */
6774 944
        return $array;
6775
    }
6776
6777
    /**
6778
     * Get the current array from the "Arrayy"-object as list.
6779
     *
6780
     * @param bool $convertAllArrayyElements <p>
6781
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6782
     *                                       </p>
6783
     *
6784
     * @return array
6785
     *
6786
     * @phpstan-return list<T>
6787
     * @psalm-mutation-free
6788
     */
6789 1
    public function toList(bool $convertAllArrayyElements = false): array
6790
    {
6791
        /** @var list<T> - currently phpstan can't return different types depending on the phpdocs params */
6792 1
        return $this->toArray(
6793 1
            $convertAllArrayyElements,
6794 1
            false
6795
        );
6796
    }
6797
6798
    /**
6799
     * Convert the current array to JSON.
6800
     *
6801
     * EXAMPLE: <code>
6802
     * a(['bar', ['foo']])->toJson(); // '["bar",{"1":"foo"}]'
6803
     * </code>
6804
     *
6805
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
6806
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
6807
     *
6808
     * @return string
6809
     */
6810 13
    public function toJson(int $options = 0, int $depth = 512): string
6811
    {
6812 13
        $return = \json_encode($this->toArray(), $options, $depth);
6813 13
        if ($return === false) {
6814
            return '';
6815
        }
6816
6817 13
        return $return;
6818
    }
6819
6820
    /**
6821
     * @param string[]|null $items  [optional]
6822
     * @param string[]      $helper [optional]
6823
     *
6824
     * @return static|static[]
6825
     *
6826
     * @phpstan-return static<int, static<TKey,T>>
6827
     */
6828 1
    public function toPermutation(array $items = null, array $helper = []): self
6829
    {
6830
        // init
6831 1
        $return = [];
6832
6833 1
        if ($items === null) {
6834 1
            $items = $this->toArray();
6835
        }
6836
6837 1
        if (empty($items)) {
6838 1
            $return[] = $helper;
6839
        } else {
6840 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
6841 1
                $new_items = $items;
6842 1
                $new_helper = $helper;
6843 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
6844
                /** @noinspection PhpSillyAssignmentInspection */
6845
                /** @var string[] $new_items */
6846 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...
6847 1
                \array_unshift($new_helper, $tmp_helper);
6848 1
                $return = \array_merge(
6849 1
                    $return,
6850 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
6851
                );
6852
            }
6853
        }
6854
6855
        /** @var static<int, static<TKey,T>> $return  - help for phpstan */
6856 1
        $return = static::create(
6857 1
            $return,
6858 1
            $this->iteratorClass,
6859 1
            false
6860
        );
6861
6862 1
        return $return;
6863
    }
6864
6865
    /**
6866
     * Implodes array to a string with specified separator.
6867
     *
6868
     * @param string $separator [optional] <p>The element's separator.</p>
6869
     *
6870
     * @return string
6871
     *                <p>The string representation of array, separated by ",".</p>
6872
     */
6873 19
    public function toString(string $separator = ','): string
6874
    {
6875 19
        return $this->implode($separator);
6876
    }
6877
6878
    /**
6879
     * Return a duplicate free copy of the current array.
6880
     *
6881
     * EXAMPLE: <code>
6882
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[1, 2]
6883
     * </code>
6884
     *
6885
     * @return $this
6886
     *               <p>(Mutable)</p>
6887
     *
6888
     * @phpstan-return static<int,T>
6889
     */
6890 13
    public function uniqueNewIndex(): self
6891
    {
6892
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6893
6894 13
        $this->array = $this->reduce(
6895
            static function ($resultArray, $value, $key) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
6896 12
                if (!\in_array($value, $resultArray, true)) {
6897 12
                    $resultArray[] = $value;
6898
                }
6899
6900 12
                return $resultArray;
6901 13
            },
6902 13
            []
6903 13
        )->toArray();
6904 13
        $this->generator = null;
6905
6906 13
        return $this;
6907
    }
6908
6909
    /**
6910
     * Return a duplicate free copy of the current array. (with the old keys)
6911
     *
6912
     * EXAMPLE: <code>
6913
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[2 => 1, 3 => 2]
6914
     * </code>
6915
     *
6916
     * @return $this
6917
     *               <p>(Mutable)</p>
6918
     *
6919
     * @phpstan-return static<TKey,T>
6920
     */
6921 11
    public function uniqueKeepIndex(): self
6922
    {
6923
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6924
6925
        // init
6926 11
        $array = $this->toArray();
6927
6928
        /**
6929
         * @psalm-suppress MissingClosureReturnType
6930
         * @psalm-suppress MissingClosureParamType
6931
         */
6932 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...
6933 11
            \array_keys($array),
6934
            static function ($resultArray, $key) use ($array) {
6935 10
                if (!\in_array($array[$key], $resultArray, true)) {
6936 10
                    $resultArray[$key] = $array[$key];
6937
                }
6938
6939 10
                return $resultArray;
6940 11
            },
6941 11
            []
6942
        );
6943 11
        $this->generator = null;
6944
6945 11
        return $this;
6946
    }
6947
6948
    /**
6949
     * alias: for "Arrayy->uniqueNewIndex()"
6950
     *
6951
     * @return static
6952
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
6953
     *
6954
     * @see          Arrayy::unique()
6955
     *
6956
     * @phpstan-return static<int,T>
6957
     */
6958 13
    public function unique(): self
6959
    {
6960 13
        return $this->uniqueNewIndex();
6961
    }
6962
6963
    /**
6964
     * Prepends one or more values to the beginning of array at once.
6965
     *
6966
     * @param mixed ...$args
6967
     *
6968
     * @return $this
6969
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
6970
     *
6971
     * @phpstan-param  array<TKey,T> ...$args
6972
     * @phpstan-return static<TKey,T>
6973
     */
6974 6 View Code Duplication
    public function unshift(...$args): 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...
6975
    {
6976 6
        $this->generatorToArray();
6977
6978
        if (
6979 6
            $this->checkPropertyTypes
6980
            &&
6981 6
            $this->properties !== []
6982
        ) {
6983 2
            foreach ($args as $key => $value) {
6984 2
                $this->checkType($key, $value);
6985
            }
6986
        }
6987
6988 5
        \array_unshift($this->array, ...$args);
6989
6990 5
        return $this;
6991
    }
6992
6993
    /**
6994
     * Tests whether the given closure return something valid for all elements of this array.
6995
     *
6996
     * @param \Closure $closure the predicate
6997
     *
6998
     * @return bool
6999
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
7000
     *
7001
     * @phpstan-param \Closure(T=,TKey=):bool $closure
7002
     */
7003 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...
7004
    {
7005 1
        foreach ($this->getGenerator() as $key => $value) {
7006 1
            if (!$closure($value, $key)) {
7007 1
                return false;
7008
            }
7009
        }
7010
7011 1
        return true;
7012
    }
7013
7014
    /**
7015
     * Get all values from a array.
7016
     *
7017
     * EXAMPLE: <code>
7018
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
7019
     * $arrayyTmp->values(); // Arrayy[0 => 'foo', 1 => 'foo2', 2 => 'bar']
7020
     * </code>
7021
     *
7022
     * @return static
7023
     *                <p>(Immutable)</p>
7024
     *
7025
     * @phpstan-return static<TKey,T>
7026
     * @psalm-mutation-free
7027
     */
7028 2
    public function values(): self
7029
    {
7030 2
        return static::create(
7031
            function () {
7032 2
                foreach ($this->getGenerator() as $value) {
7033 2
                    yield $value;
7034
                }
7035 2
            },
7036 2
            $this->iteratorClass,
7037 2
            false
7038
        );
7039
    }
7040
7041
    /**
7042
     * Apply the given function to every element in the array, discarding the results.
7043
     *
7044
     * EXAMPLE: <code>
7045
     * $callable = function (&$value, $key) {
7046
     *     $value = $key;
7047
     * };
7048
     * $arrayy = a([1, 2, 3]);
7049
     * $arrayy->walk($callable); // Arrayy[0, 1, 2]
7050
     * </code>
7051
     *
7052
     * @param callable $callable
7053
     * @param bool     $recursive
7054
     *                            [optional] <p>Whether array will be walked recursively or no</p>
7055
     * @param mixed    $userData
7056
     *                            [optional] <p>
7057
     *                            If the optional $userData parameter is supplied,
7058
     *                            it will be passed as the third parameter to the $callable.
7059
     *                            </p>
7060
     *
7061
     * @return $this
7062
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
7063
     *
7064
     * @template TExtra
7065
     *              <p>The extra input value type.</p>
7066
     *
7067
     * @phostan-param TExtra $userData
7068
     * @phpstan-param  callable(T,TKey,?TExtra):void $callable
7069
     * @phpstan-return static<TKey,T>
7070
     */
7071 12
    public function walk(
7072
        $callable,
7073
        bool $recursive = false,
7074
        $userData = self::ARRAYY_HELPER_WALK
7075
    ): self {
7076 12
        $this->generatorToArray();
7077
7078 12
        if ($this->array !== []) {
7079 10
            if ($recursive === true) {
7080 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
7081
                    \array_walk_recursive($this->array, $callable, $userData);
7082
                } else {
7083 5
                    \array_walk_recursive($this->array, $callable);
7084
                }
7085
            } else {
7086
                /** @noinspection NestedPositiveIfStatementsInspection */
7087 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
7088
                    \array_walk($this->array, $callable, $userData);
7089
                } else {
7090 5
                    \array_walk($this->array, $callable);
7091
                }
7092
            }
7093
        }
7094
7095 12
        return $this;
7096
    }
7097
7098
    /**
7099
     * Returns a collection of matching items.
7100
     *
7101
     * @param string $keyOrPropertyOrMethod
7102
     *                                      <p>The property or method to evaluate.</p>
7103
     * @param mixed  $value
7104
     *                                      <p>The value to match.</p>
7105
     *
7106
     * @throws \InvalidArgumentException if property or method is not defined
7107
     *
7108
     * @return static
7109
     *
7110
     * @phpstan-return static<TKey,T>
7111
     */
7112 1
    public function where(string $keyOrPropertyOrMethod, $value): self
7113
    {
7114 1
        return $this->filter(
7115
            function ($item) use ($keyOrPropertyOrMethod, $value) {
7116
                $accessorValue = $this->extractValue(
7117
                    $item,
7118
                    $keyOrPropertyOrMethod
7119
                );
7120
7121
                return $accessorValue === $value;
7122 1
            }
7123
        );
7124
    }
7125
7126
    /**
7127
     * Convert an array into a object.
7128
     *
7129
     * @param array $array
7130
     *
7131
     * @return \stdClass
7132
     *
7133
     * @phpstan-param array<int|string,mixed> $array
7134
     */
7135 4
    final protected static function arrayToObject(array $array = []): \stdClass
7136
    {
7137
        // init
7138 4
        $object = new \stdClass();
7139
7140 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
7141 1
            return $object;
7142
        }
7143
7144 3
        foreach ($array as $name => $value) {
7145 3
            if (\is_array($value)) {
7146 1
                $object->{$name} = static::arrayToObject($value);
7147
            } else {
7148 3
                $object->{$name} = $value;
7149
            }
7150
        }
7151
7152 3
        return $object;
7153
    }
7154
7155
    /**
7156
     * @param array|\Generator|null $input         <p>
7157
     *                                             An array containing keys to return.
7158
     *                                             </p>
7159
     * @param mixed|null            $search_values [optional] <p>
7160
     *                                             If specified, then only keys containing these values are returned.
7161
     *                                             </p>
7162
     * @param bool                  $strict        [optional] <p>
7163
     *                                             Determines if strict comparison (===) should be used during the
7164
     *                                             search.
7165
     *                                             </p>
7166
     *
7167
     * @return array
7168
     *               <p>An array of all the keys in input.</p>
7169
     *
7170
     * @template TInput
7171
     *
7172
     * @phpstan-param  array<array-key,TInput>|\Generator<array-key,TInput>|null $input
7173
     * @phpstan-return array<array-key>|array<TKey>
7174
     *
7175
     * @psalm-mutation-free
7176
     */
7177 11
    protected function array_keys_recursive(
7178
        $input = null,
7179
        $search_values = null,
7180
        bool $strict = true
7181
    ): array {
7182
        // init
7183 11
        $keys = [];
7184 11
        $keysTmp = [];
7185
7186 11
        if ($input === null) {
7187 4
            $input = $this->getGenerator();
7188
        }
7189
7190 11
        if ($search_values === null) {
7191 11
            foreach ($input as $key => $value) {
7192 11
                $keys[] = $key;
7193
7194
                // check if recursive is needed
7195 11
                if (\is_array($value)) {
7196 4
                    $keysTmp[] = $this->array_keys_recursive($value);
7197
                }
7198
            }
7199
        } else {
7200 1
            $is_array_tmp = \is_array($search_values);
7201
7202 1
            foreach ($input as $key => $value) {
7203 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...
7204
                    (
7205 1
                        $is_array_tmp === false
7206
                        &&
7207 1
                        $strict === true
7208
                        &&
7209 1
                        $search_values === $value
7210
                    )
7211
                    ||
7212
                    (
7213 1
                        $is_array_tmp === false
7214
                        &&
7215 1
                        $strict === false
7216
                        &&
7217 1
                        $search_values == $value
7218
                    )
7219
                    ||
7220
                    (
7221 1
                        $is_array_tmp === true
7222
                        &&
7223 1
                        \in_array($value, $search_values, $strict)
7224
                    )
7225
                ) {
7226 1
                    $keys[] = $key;
7227
                }
7228
7229
                // check if recursive is needed
7230 1
                if (\is_array($value)) {
7231 1
                    $keysTmp[] = $this->array_keys_recursive($value);
7232
                }
7233
            }
7234
        }
7235
7236 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
7237
    }
7238
7239
    /**
7240
     * @param string     $path
7241
     * @param callable   $callable
7242
     * @param array|null $currentOffset
7243
     *
7244
     * @return void
7245
     *
7246
     * @phpstan-param array<TKey,T>|null $currentOffset
7247
     * @psalm-mutation-free
7248
     */
7249 10
    protected function callAtPath($path, $callable, &$currentOffset = null)
7250
    {
7251 10
        $this->generatorToArray();
7252
7253 10
        if ($currentOffset === null) {
7254 10
            $currentOffset = &$this->array;
7255
        }
7256
7257 10
        $explodedPath = \explode($this->pathSeparator, $path);
7258 10
        if ($explodedPath === false) {
7259
            return;
7260
        }
7261
7262 10
        $nextPath = \array_shift($explodedPath);
7263
7264 10
        if (!isset($currentOffset[$nextPath])) {
7265 1
            return;
7266
        }
7267
7268 9
        if (!empty($explodedPath)) {
7269 1
            $this->callAtPath(
7270 1
                \implode($this->pathSeparator, $explodedPath),
7271 1
                $callable,
7272 1
                $currentOffset[$nextPath]
7273
            );
7274
        } else {
7275 9
            $callable($currentOffset[$nextPath]);
7276
        }
7277 9
    }
7278
7279
    /**
7280
     * Extracts the value of the given property or method from the object.
7281
     *
7282
     * @param static<T> $object
0 ignored issues
show
Documentation introduced by
The doc-type static<T> 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...
7283
     *                                         <p>The object to extract the value from.</p>
7284
     * @param string    $keyOrPropertyOrMethod
7285
     *                                         <p>The property or method for which the
7286
     *                                         value should be extracted.</p>
7287
     *
7288
     * @throws \InvalidArgumentException if the method or property is not defined
7289
     *
7290
     * @return mixed
7291
     *               <p>The value extracted from the specified property or method.</p>
7292
     *
7293
     * @phpstan-param self<TKey,T> $object
7294
     */
7295 1
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
7296
    {
7297 1
        if (isset($object[$keyOrPropertyOrMethod])) {
7298 1
            $return = $object->get($keyOrPropertyOrMethod);
7299
7300 1
            if ($return instanceof self) {
7301
                return $return->toArray();
7302
            }
7303
7304 1
            return $return;
7305
        }
7306
7307
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
7308
            return $object->{$keyOrPropertyOrMethod};
7309
        }
7310
7311
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
7312
            return $object->{$keyOrPropertyOrMethod}();
7313
        }
7314
7315
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
7316
    }
7317
7318
    /**
7319
     * create a fallback for array
7320
     *
7321
     * 1. use the current array, if it's a array
7322
     * 2. fallback to empty array, if there is nothing
7323
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
7324
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
7325
     * 5. call "__toArray()" on object, if the method exists
7326
     * 6. cast a string or object with "__toString()" into an array
7327
     * 7. throw a "InvalidArgumentException"-Exception
7328
     *
7329
     * @param mixed $data
7330
     *
7331
     * @throws \InvalidArgumentException
7332
     *
7333
     * @return array
7334
     *
7335
     * @phpstan-return array<mixed>|array<TKey,T>
7336
     */
7337 1215
    protected function fallbackForArray(&$data): array
7338
    {
7339 1215
        $data = $this->internalGetArray($data);
7340
7341 1215
        if ($data === null) {
7342 2
            throw new \InvalidArgumentException('Passed value should be a array');
7343
        }
7344
7345 1213
        return $data;
7346
    }
7347
7348
    /**
7349
     * @param bool $preserveKeys <p>
7350
     *                           e.g.: A generator maybe return the same key more then once,
7351
     *                           so maybe you will ignore the keys.
7352
     *                           </p>
7353
     *
7354
     * @return bool
7355
     *
7356
     * @noinspection ReturnTypeCanBeDeclaredInspection
7357
     * @psalm-mutation-free :/
7358
     */
7359 1124
    protected function generatorToArray(bool $preserveKeys = true)
7360
    {
7361 1124
        if ($this->generator) {
7362 3
            $this->array = $this->toArray(false, $preserveKeys);
7363 3
            $this->generator = null;
7364
7365 3
            return true;
7366
        }
7367
7368 1124
        return false;
7369
    }
7370
7371
    /**
7372
     * Get correct PHP constant for direction.
7373
     *
7374
     * @param int|string $direction
7375
     *
7376
     * @return int
7377
     * @psalm-mutation-free
7378
     */
7379 43
    protected function getDirection($direction): int
7380
    {
7381 43
        if ((string) $direction === $direction) {
7382 10
            $direction = \strtolower($direction);
7383
7384 10
            if ($direction === 'desc') {
7385 2
                $direction = \SORT_DESC;
7386
            } else {
7387 9
                $direction = \SORT_ASC;
7388
            }
7389
        }
7390
7391
        if (
7392 43
            $direction !== \SORT_DESC
7393
            &&
7394 43
            $direction !== \SORT_ASC
7395
        ) {
7396
            $direction = \SORT_ASC;
7397
        }
7398
7399 43
        return $direction;
7400
    }
7401
7402
    /**
7403
     * @return TypeCheckInterface[]
7404
     *
7405
     * @noinspection ReturnTypeCanBeDeclaredInspection
7406
     */
7407 25
    protected function getPropertiesFromPhpDoc()
7408
    {
7409 25
        static $PROPERTY_CACHE = [];
7410 25
        $cacheKey = 'Class::' . static::class;
7411
7412 25
        if (isset($PROPERTY_CACHE[$cacheKey])) {
7413 23
            return $PROPERTY_CACHE[$cacheKey];
7414
        }
7415
7416
        // init
7417 4
        $properties = [];
7418
7419 4
        $reflector = new \ReflectionClass($this);
7420 4
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
7421 4
        $docComment = $reflector->getDocComment();
7422 4 View Code Duplication
        if ($docComment) {
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...
7423 4
            $docblock = $factory->create($docComment);
7424
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7425 4
            foreach ($docblock->getTagsByName('property') as $tag) {
7426 3
                $typeName = $tag->getVariableName();
7427
                /** @var string|null $typeName */
7428 3
                if ($typeName !== null) {
7429 3
                    $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7430 3
                    if ($typeCheckPhpDoc !== null) {
7431 3
                        $properties[$typeName] = $typeCheckPhpDoc;
7432
                    }
7433
                }
7434
            }
7435
        }
7436
7437
        /** @noinspection PhpAssignmentInConditionInspection */
7438 4
        while ($reflector = $reflector->getParentClass()) {
7439 4
            $docComment = $reflector->getDocComment();
7440 4 View Code Duplication
            if ($docComment) {
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...
7441 4
                $docblock = $factory->create($docComment);
7442
                /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7443 4
                foreach ($docblock->getTagsByName('property') as $tag) {
7444 1
                    $typeName = $tag->getVariableName();
7445
                    /** @var string|null $typeName */
7446 1
                    if ($typeName !== null) {
7447 1
                        if (isset($properties[$typeName])) {
7448 1
                            continue;
7449
                        }
7450
7451 1
                        $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7452 1
                        if ($typeCheckPhpDoc !== null) {
7453 1
                            $properties[$typeName] = $typeCheckPhpDoc;
7454
                        }
7455
                    }
7456
                }
7457
            }
7458
        }
7459
7460 4
        return $PROPERTY_CACHE[$cacheKey] = $properties;
7461
    }
7462
7463
    /**
7464
     * @param string $glue
7465
     * @param mixed  $pieces
7466
     * @param bool   $useKeys
7467
     *
7468
     * @return string
7469
     *
7470
     * @phpstan-param scalar|object|self<TKey|T>|array<TKey,T> $pieces
7471
     * @psalm-mutation-free
7472
     */
7473 37
    protected function implode_recursive(
7474
        $glue = '',
7475
        $pieces = [],
7476
        bool $useKeys = false
7477
    ): string {
7478 37
        if ($pieces instanceof self) {
7479 1
            $pieces = $pieces->toArray();
7480
        }
7481
7482 37
        if (\is_array($pieces)) {
7483
            /** @noinspection PhpSillyAssignmentInspection - hack for phpstan */
7484
            /** @phpstan-var array<TKey,T> $pieces */
7485 37
            $pieces = $pieces;
0 ignored issues
show
Bug introduced by
Why assign $pieces to itself?

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

This assignement can be removed without consequences.

Loading history...
7486
7487 37
            $pieces_count = \count($pieces, \COUNT_NORMAL);
7488 37
            $pieces_count_not_zero = $pieces_count > 0;
7489
7490 37
            return \implode(
7491 37
                $glue,
7492 37
                \array_map(
7493 37
                    [$this, 'implode_recursive'],
7494 37
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
7495 37
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
7496
                )
7497
            );
7498
        }
7499
7500
        if (
7501 37
            \is_scalar($pieces) === true
7502
            ||
7503 37
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
7504
        ) {
7505 33
            return (string) $pieces;
7506
        }
7507
7508 8
        return '';
7509
    }
7510
7511
    /**
7512
     * @param mixed                 $needle   <p>
7513
     *                                        The searched value.
7514
     *                                        </p>
7515
     *                                        <p>
7516
     *                                        If needle is a string, the comparison is done
7517
     *                                        in a case-sensitive manner.
7518
     *                                        </p>
7519
     * @param array|\Generator|null $haystack <p>
7520
     *                                        The array.
7521
     *                                        </p>
7522
     * @param bool                  $strict   [optional] <p>
7523
     *                                        If the third parameter strict is set to true
7524
     *                                        then the in_array function will also check the
7525
     *                                        types of the
7526
     *                                        needle in the haystack.
7527
     *                                        </p>
7528
     *
7529
     * @return bool
7530
     *              <p>true if needle is found in the array, false otherwise</p>
7531
     *
7532
     * @phpstan-param (array&T)|array<TKey,T>|\Generator<TKey,T>|null $haystack
7533
     *
7534
     * @psalm-mutation-free
7535
     */
7536 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
7537
    {
7538 18
        if ($haystack === null) {
7539
            $haystack = $this->getGenerator();
7540
        }
7541
7542 18
        foreach ($haystack as $item) {
7543 14
            if (\is_array($item)) {
7544 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
7545
            } else {
7546
                /** @noinspection NestedPositiveIfStatementsInspection */
7547 14
                if ($strict === true) {
7548 14
                    $returnTmp = $item === $needle;
7549
                } else {
7550
                    $returnTmp = $item == $needle;
7551
                }
7552
            }
7553
7554 14
            if ($returnTmp === true) {
7555 10
                return true;
7556
            }
7557
        }
7558
7559 8
        return false;
7560
    }
7561
7562
    /**
7563
     * @param mixed $data
7564
     *
7565
     * @return array<mixed>|null
7566
     */
7567 1215
    protected function internalGetArray(&$data)
7568
    {
7569 1215
        if (\is_array($data)) {
7570 1209
            return $data;
7571
        }
7572
7573 111
        if (!$data) {
7574 7
            return [];
7575
        }
7576
7577 110
        if (\is_object($data) === true) {
7578 103
            if ($data instanceof \ArrayObject) {
7579 5
                return $data->getArrayCopy();
7580
            }
7581
7582 99
            if ($data instanceof \Generator) {
7583
                return static::createFromGeneratorImmutable($data)->toArray();
7584
            }
7585
7586 99
            if ($data instanceof \Traversable) {
7587
                return static::createFromObject($data)->toArray();
7588
            }
7589
7590 99
            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...
7591
                return (array) $data->jsonSerialize();
7592
            }
7593
7594 99
            if (\method_exists($data, '__toArray')) {
7595
                return (array) $data->__toArray();
7596
            }
7597
7598 99
            if (\method_exists($data, '__toString')) {
7599
                return [(string) $data];
7600
            }
7601
        }
7602
7603 106
        if (\is_callable($data)) {
7604
            /**
7605
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
7606
             */
7607 97
            $this->generator = new ArrayyRewindableGenerator($data);
7608
7609 97
            return [];
7610
        }
7611
7612 11
        if (\is_scalar($data)) {
7613 9
            return [$data];
7614
        }
7615
7616 2
        return null;
7617
    }
7618
7619
    /**
7620
     * Internal mechanics of remove method.
7621
     *
7622
     * @param float|int|string $key
7623
     *
7624
     * @return bool
7625
     */
7626 22
    protected function internalRemove($key): bool
7627
    {
7628 22
        $this->generatorToArray();
7629
7630
        if (
7631 22
            $this->pathSeparator
7632
            &&
7633 22
            (string) $key === $key
7634
            &&
7635 22
            \strpos($key, $this->pathSeparator) !== false
7636
        ) {
7637
            $path = \explode($this->pathSeparator, (string) $key);
7638
7639
            if ($path !== false) {
7640
                // crawl though the keys
7641
                while (\count($path, \COUNT_NORMAL) > 1) {
7642
                    $key = \array_shift($path);
7643
7644
                    if (!$this->has($key)) {
7645
                        return false;
7646
                    }
7647
7648
                    $this->array = &$this->array[$key];
7649
                }
7650
7651
                $key = \array_shift($path);
7652
            }
7653
        }
7654
7655 22
        unset($this->array[$key]);
7656
7657 22
        return true;
7658
    }
7659
7660
    /**
7661
     * Internal mechanic of set method.
7662
     *
7663
     * @param int|string|null $key
7664
     * @param mixed           $value
7665
     * @param bool            $checkProperties
7666
     *
7667
     * @return bool
7668
     */
7669 1065
    protected function internalSet(
7670
        $key,
7671
        &$value,
7672
        bool $checkProperties = true
7673
    ): bool {
7674
        if (
7675 1065
            $checkProperties === true
7676
            &&
7677 1065
            $this->properties !== []
7678
        ) {
7679 118
            $this->checkType($key, $value);
7680
        }
7681
7682 1063
        if ($key === null) {
7683
            return false;
7684
        }
7685
7686 1063
        $this->generatorToArray();
7687
7688 1063
        $array = &$this->array;
7689
7690
        /**
7691
         * https://github.com/vimeo/psalm/issues/2536
7692
         *
7693
         * @psalm-suppress PossiblyInvalidArgument
7694
         * @psalm-suppress InvalidScalarArgument
7695
         */
7696
        if (
7697 1063
            $this->pathSeparator
7698
            &&
7699 1063
            (string) $key === $key
7700
            &&
7701 1063
            \strpos($key, $this->pathSeparator) !== false
7702
        ) {
7703 9
            $path = \explode($this->pathSeparator, (string) $key);
7704
7705 9
            if ($path !== false) {
7706
                // crawl through the keys
7707 9
                while (\count($path, \COUNT_NORMAL) > 1) {
7708 9
                    $key = \array_shift($path);
7709
7710 9
                    $array = &$array[$key];
7711
                }
7712
7713 9
                $key = \array_shift($path);
7714
            }
7715
        }
7716
7717 1063
        if ($array === null) {
7718 4
            $array = [];
7719 1060
        } elseif (!\is_array($array)) {
7720 1
            throw new \RuntimeException('Can not set value at this path "' . $key . '" because (' . \gettype($array) . ')"' . \print_r($array, true) . '" is not an array.');
7721
        }
7722
7723 1063
        $array[$key] = $value;
7724
7725 1063
        return true;
7726
    }
7727
7728
    /**
7729
     * Convert a object into an array.
7730
     *
7731
     * @param mixed|object $object
7732
     *
7733
     * @return array|mixed
7734
     *
7735
     * @psalm-mutation-free
7736
     */
7737 5
    protected static function objectToArray($object)
7738
    {
7739 5
        if (!\is_object($object)) {
7740 4
            return $object;
7741
        }
7742
7743 5
        $object = \get_object_vars($object);
7744
7745
        /**
7746
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
7747
         */
7748 5
        return \array_map(['static', 'objectToArray'], $object);
7749
    }
7750
7751
    /**
7752
     * @param array $data
7753
     * @param bool  $checkPropertiesInConstructor
7754
     *
7755
     * @return void
7756
     *
7757
     * @phpstan-param array<mixed,T> $data
7758
     */
7759 1213
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
7760
    {
7761 1213
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
7762
                                        &&
7763 1213
                                        $checkPropertiesInConstructor === true;
7764
7765 1213
        if ($this->properties !== []) {
7766 105
            foreach ($data as $key => &$valueInner) {
7767 105
                $this->internalSet(
7768 105
                    $key,
7769 105
                    $valueInner,
7770 105
                    $checkPropertiesInConstructor
7771
                );
7772
            }
7773
        } else {
7774
            if (
7775 1128
                $this->checkPropertyTypes === true
7776
                ||
7777 1128
                $checkPropertiesInConstructor === true
7778
            ) {
7779 24
                $this->properties = $this->getPropertiesFromPhpDoc();
7780
            }
7781
7782
            /** @var TypeCheckInterface[] $properties */
7783 1128
            $properties = $this->properties;
7784
7785
            if (
7786 1128
                $this->checkPropertiesMismatchInConstructor === true
7787
                &&
7788 1128
                \count($data) !== 0
7789
                &&
7790 1128
                \count(\array_diff_key($properties, $data)) > 0
7791
            ) {
7792 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...
7793
            }
7794
7795 1127
            foreach ($data as $key => &$valueInner) {
7796 956
                $this->internalSet(
7797 956
                    $key,
7798 956
                    $valueInner,
7799 956
                    $checkPropertiesInConstructor
7800
                );
7801
            }
7802
        }
7803 1205
    }
7804
7805
    /**
7806
     * sorting keys
7807
     *
7808
     * @param array      $elements
7809
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7810
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7811
     *                              <strong>SORT_NATURAL</strong></p>
7812
     *
7813
     * @return $this
7814
     *               <p>(Mutable) Return this Arrayy object.</p>
7815
     *
7816
     * @phpstan-param  array<mixed|TKey,T> $elements
7817
     * @phpstan-return static<TKey,T>
7818
     */
7819 18
    protected function sorterKeys(
7820
        array &$elements,
7821
        $direction = \SORT_ASC,
7822
        int $strategy = \SORT_REGULAR
7823
    ): self {
7824 18
        $direction = $this->getDirection($direction);
7825
7826 18
        switch ($direction) {
7827 18
            case 'desc':
7828
            case \SORT_DESC:
7829 6
                \krsort($elements, $strategy);
7830
7831 6
                break;
7832 13
            case 'asc':
7833 13
            case \SORT_ASC:
7834
            default:
7835 13
                \ksort($elements, $strategy);
7836
        }
7837
7838 18
        return $this;
7839
    }
7840
7841
    /**
7842
     * @param array      $elements  <p>Warning: used as reference</p>
7843
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7844
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7845
     *                              <strong>SORT_NATURAL</strong></p>
7846
     * @param bool       $keepKeys
7847
     *
7848
     * @return $this
7849
     *               <p>(Mutable) Return this Arrayy object.</p>
7850
     *
7851
     * @phpstan-param array<mixed|TKey,T> $elements
7852
     * @phpstan-return static<int|TKey,T>
7853
     */
7854 24
    protected function sorting(
7855
        array &$elements,
7856
        $direction = \SORT_ASC,
7857
        int $strategy = \SORT_REGULAR,
7858
        bool $keepKeys = false
7859
    ): self {
7860 24
        $direction = $this->getDirection($direction);
7861
7862 24
        if (!$strategy) {
7863 24
            $strategy = \SORT_REGULAR;
7864
        }
7865
7866 24
        switch ($direction) {
7867 24
            case 'desc':
7868
            case \SORT_DESC:
7869 13
                if ($keepKeys) {
7870 9
                    \arsort($elements, $strategy);
7871
                } else {
7872 4
                    \rsort($elements, $strategy);
7873
                }
7874
7875 13
                break;
7876 11
            case 'asc':
7877 11
            case \SORT_ASC:
7878
            default:
7879 11
                if ($keepKeys) {
7880 4
                    \asort($elements, $strategy);
7881
                } else {
7882 7
                    \sort($elements, $strategy);
7883
                }
7884
        }
7885
7886 24
        return $this;
7887
    }
7888
7889
    /**
7890
     * @param array $array
7891
     *
7892
     * @return array
7893
     *
7894
     * @psalm-mutation-free
7895
     */
7896 25
    private function getArrayRecursiveHelperArrayy(array $array)
7897
    {
7898 25
        if ($array === []) {
7899
            return [];
7900
        }
7901
7902 25
        \array_walk_recursive(
7903 25
            $array,
7904
            /**
7905
             * @param array|self $item
7906
             *
7907
             * @return void
7908
             */
7909
            static function (&$item) {
7910 25
                if ($item instanceof self) {
7911 1
                    $item = $item->getArray();
7912
                }
7913 25
            }
7914
        );
7915
7916 25
        return $array;
7917
    }
7918
7919
    /**
7920
     * @param int|string|null $key
7921
     * @param mixed           $value
7922
     *
7923
     * @return void
7924
     */
7925 118
    private function checkType($key, $value)
7926
    {
7927
        if (
7928 118
            $key !== null
7929
            &&
7930 118
            isset($this->properties[$key]) === false
7931
            &&
7932 118
            $this->checkPropertiesMismatch === true
7933
        ) {
7934
            throw new \TypeError('The key "' . $key . '" does not exists as "@property" phpdoc. (' . \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 . '" ...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...
7935
        }
7936
7937 118
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
7938 103
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
7939 25
        } elseif ($key !== null && isset($this->properties[$key])) {
7940 25
            $this->properties[$key]->checkType($value);
7941
        }
7942 116
    }
7943
}
7944