• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tempestphp / tempest-framework / 11714560237

06 Nov 2024 06:46PM UTC coverage: 82.608% (+0.003%) from 82.605%
11714560237

push

github

web-flow
feat(container): support injecting properties using `#[Inject]` (#690)

6 of 6 new or added lines in 2 files covered. (100.0%)

95 existing lines in 9 files now uncovered.

7210 of 8728 relevant lines covered (82.61%)

49.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.28
/src/Tempest/Support/src/ArrayHelper.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Tempest\Support;
6

7
use ArrayAccess;
8
use Closure;
9
use Countable;
10
use Generator;
11
use InvalidArgumentException;
12
use Iterator;
13
use Random\Randomizer;
14
use Serializable;
15
use Stringable;
16
use function Tempest\map;
17

18
/**
19
 * @template TKey of array-key
20
 * @template TValue
21
 *
22
 * @implements ArrayAccess<TKey, TValue>
23
 * @implements Iterator<TKey, TValue>
24
 */
25
final class ArrayHelper implements Iterator, ArrayAccess, Serializable, Countable
26
{
27
    use IsIterable;
28

29
    /** @var array<TKey, TValue> */
30
    private array $array;
31

32
    /**
33
     * @param array<TKey, TValue>|self<TKey, TValue>|TValue $input
34
     */
35
    public function __construct(
457✔
36
        mixed $input = [],
37
    ) {
38
        if (is_array($input)) {
457✔
39
            $this->array = $input;
441✔
40
        } elseif ($input instanceof self) {
174✔
41
            $this->array = $input->array;
2✔
42
        } else {
43
            $this->array = [$input];
173✔
44
        }
45
    }
46

47
    /**
48
     * Gets a value from the array and remove it.
49
     *
50
     * @param array-key $key
51
     */
52
    public function pull(string|int $key, mixed $default = null): mixed
1✔
53
    {
54
        $value = $this->get($key, $default);
1✔
55

56
        $this->remove($key);
1✔
57

58
        return $value;
1✔
59
    }
60

61
    /**
62
     * Shuffles the array.
63
     *
64
     * @return self<TKey, TValue>
65
     */
66
    public function shuffle(): self
2✔
67
    {
68
        return new self((new Randomizer())->shuffleArray($this->array));
2✔
69
    }
70

71
    /**
72
     * @alias of `remove`.
73
     */
74
    public function forget(string|int|array $keys): self
1✔
75
    {
76
        return $this->remove($keys);
1✔
77
    }
78

79
    /**
80
     * Removes the specified items from the array.
81
     *
82
     * @param array-key|array<array-key> $keys The keys of the items to remove.
83
     *
84
     * @return self<TKey, TValue>
85
     */
86
    public function remove(string|int|array $keys): self
5✔
87
    {
88
        $keys = is_array($keys) ? $keys : [$keys];
5✔
89

90
        foreach ($keys as $key) {
5✔
91
            $this->offsetUnset($key);
5✔
92
        }
93

94
        return $this;
5✔
95
    }
96

97
    /**
98
     * Asserts whether the array is a list.
99
     * An array is a list if its keys consist of consecutive numbers.
100
     */
101
    public function isList(): bool
13✔
102
    {
103
        return array_is_list($this->array);
13✔
104
    }
105

106
    /**
107
     * Asserts whether the array is a associative.
108
     * An array is associative if its keys do not consist of consecutive numbers.
109
     */
110
    public function isAssoc(): bool
12✔
111
    {
112
        return ! $this->isList();
12✔
113
    }
114

115
    /**
116
     * Gets one or a specified number of random values from the array.
117
     *
118
     * @param int $number The number of random values to get.
119
     * @param bool $preserveKey Whether to include the keys of the original array.
120
     *
121
     * @return self<TKey, TValue>|mixed The random values, or a single value if `$number` is 1.
122
     */
123
    public function random(int $number = 1, bool $preserveKey = false): mixed
5✔
124
    {
125
        $count = count($this->array);
5✔
126

127
        if ($number > $count) {
5✔
128
            throw new InvalidArgumentException("Cannot retrive {$number} items from an array of {$count} items.");
2✔
129
        }
130

131
        if ($number < 1) {
3✔
132
            throw new InvalidArgumentException("Random value only accepts positive integers, {$number} requested.");
1✔
133
        }
134

135
        $keys = (new Randomizer())->pickArrayKeys($this->array, $number);
2✔
136

137
        $randomValues = [];
2✔
138
        foreach ($keys as $key) {
2✔
139
            $preserveKey
2✔
140
                ? $randomValues[$key] = $this->array[$key]
1✔
141
                : $randomValues[] = $this->array[$key];
1✔
142
        }
143

144
        if ($preserveKey === false) {
2✔
145
            shuffle($randomValues);
1✔
146
        }
147

148
        return count($randomValues) > 1
2✔
149
            ? new self($randomValues)
2✔
150
            : $randomValues[0];
2✔
151
    }
152

153
    /**
154
     * Retrieves values from a given key in each sub-array of the current array.
155
     * Optionally, you can pass a second parameter to also get the keys following the same pattern.
156
     *
157
     * @param string $value The key to assign the values from, support dot notation.
158
     * @param string|null $key The key to assign the keys from, support dot notation.
159
     *
160
     * @return self<TKey, TValue>
161
     */
162
    public function pluck(string $value, ?string $key = null): self
3✔
163
    {
164
        $results = [];
3✔
165

166
        foreach ($this->array as $item) {
3✔
167
            if (! is_array($item)) {
3✔
168
                continue;
1✔
169
            }
170

171
            $itemValue = arr($item)->get($value);
2✔
172

173
            /**
174
             * Perform basic pluck if no key is given.
175
             * Otherwise, also pluck the key as well.
176
             */
177
            if (is_null($key)) {
2✔
178
                $results[] = $itemValue;
2✔
179
            } else {
180
                $itemKey = arr($item)->get($key);
2✔
181
                $results[$itemKey] = $itemValue;
2✔
182
            }
183
        }
184

185
        return new self($results);
3✔
186
    }
187

188
    /**
189
     * @alias of `add`.
190
     */
191
    public function push(mixed $value): self
1✔
192
    {
193
        return $this->add($value);
1✔
194
    }
195

196
    /**
197
     * Appends the specified value to the array.
198
     *
199
     * @return self<TKey, TValue>
200
     */
201
    public function add(mixed $value): self
2✔
202
    {
203
        $this->array[] = $value;
2✔
204

205
        return $this;
2✔
206
    }
207

208
    /**
209
     * Pads the array to the specified size with a value.
210
     *
211
     * @return self<TKey, TValue>
212
     */
213
    public function pad(int $size, mixed $value): self
1✔
214
    {
215
        return new self(array_pad($this->array, $size, $value));
1✔
216
    }
217

218
    /**
219
     * Reverses the keys and values of the array.
220
     *
221
     * @return self<TValue&array-key, TKey>
222
     */
223
    public function flip(): self
1✔
224
    {
225
        return new self(array_flip($this->array));
1✔
226
    }
227

228
    /**
229
     * Returns a new instance with only unique items from the original array.
230
     *
231
     * @param string|null $key The key to use as the uniqueness criteria in nested arrays.
232
     * @param bool $shouldBeStrict Whether the comparison should be strict, only used when giving a key parameter.
233
     *
234
     * @return self<TKey, TValue>
235
     */
236
    public function unique(?string $key = null, bool $shouldBeStrict = false): self
9✔
237
    {
238
        if (is_null($key) && $shouldBeStrict === false) {
9✔
239
            return new self(array_unique($this->array, flags: SORT_REGULAR));
4✔
240
        }
241

242
        $uniqueItems = [];
5✔
243
        $uniqueFilteredValues = [];
5✔
244
        foreach ($this->array as $item) {
5✔
245
            // Ensure we don't check raw values with key filter
246
            if (! is_null($key) && ! is_array($item)) {
5✔
247
                continue;
1✔
248
            }
249

250
            $filterValue = is_array($item)
5✔
251
                ? arr($item)->get($key)
4✔
252
                : $item;
1✔
253

254
            if (is_null($filterValue)) {
5✔
255
                continue;
×
256
            }
257

258
            if (in_array($filterValue, $uniqueFilteredValues, strict: $shouldBeStrict)) {
5✔
259
                continue;
4✔
260
            }
261

262
            $uniqueItems[] = $item;
5✔
263
            $uniqueFilteredValues[] = $filterValue;
5✔
264
        }
265

266
        return new self($uniqueItems);
5✔
267
    }
268

269
    /**
270
     * Returns a new instance of the array with only the items that are not present in any of the given arrays.
271
     *
272
     * @param array<TKey, TValue>|self<TKey, TValue> ...$arrays
273
     *
274
     * @return self<TKey, TValue>
275
     */
276
    public function diff(array|self ...$arrays): self
1✔
277
    {
278
        $arrays = array_map(fn (array|self $array) => $array instanceof self ? $array->toArray() : $array, $arrays);
1✔
279

280
        return new self(array_diff($this->array, ...$arrays));
1✔
281
    }
282

283
    /**
284
     * Returns a new instance of the array with only the items whose keys are not present in any of the given arrays.
285
     *
286
     * @param array<TKey, TValue>|self<TKey, TValue> ...$arrays
287
     *
288
     * @return self<TKey, TValue>
289
     */
290
    public function diffKeys(array|self ...$arrays): self
1✔
291
    {
292
        $arrays = array_map(fn (array|self $array) => $array instanceof self ? $array->toArray() : $array, $arrays);
1✔
293

294
        return new self(array_diff_key($this->array, ...$arrays));
1✔
295
    }
296

297
    /**
298
     * Returns a new instance of the array with only the items that are present in all of the given arrays.
299
     *
300
     * @param array<TKey, TValue>|self<TKey, TValue> ...$arrays
301
     *
302
     * @return self<TKey, TValue>
303
     */
304
    public function intersect(array|self ...$arrays): self
1✔
305
    {
306
        $arrays = array_map(fn (array|self $array) => $array instanceof self ? $array->toArray() : $array, $arrays);
1✔
307

308
        return new self(array_intersect($this->array, ...$arrays));
1✔
309
    }
310

311
    /**
312
     * Returns a new instance of the array with only the items whose keys are present in all of the given arrays.
313
     *
314
     * @param array<TKey, TValue>|self<TKey, TValue> ...$arrays
315
     *
316
     * @return self<TKey, TValue>
317
     */
318
    public function intersectKeys(array|self ...$arrays): self
1✔
319
    {
320
        $arrays = array_map(fn (array|self $array) => $array instanceof self ? $array->toArray() : $array, $arrays);
1✔
321

322
        return new self(array_intersect_key($this->array, ...$arrays));
1✔
323
    }
324

325
    /**
326
     * Merges the array with the given arrays.
327
     *
328
     * @param array<TKey, TValue>|self<TKey, TValue> ...$arrays The arrays to merge.
329
     *
330
     * @return self<TKey, TValue>
331
     */
332
    public function merge(array|self ...$arrays): self
2✔
333
    {
334
        $arrays = array_map(fn (array|self $array) => $array instanceof self ? $array->toArray() : $array, $arrays);
2✔
335

336
        return new self(array_merge($this->array, ...$arrays));
2✔
337
    }
338

339
    /**
340
     * Creates a new array with this current array values as keys and the given values as values.
341
     *
342
     * @template TCombineValue
343
     *
344
     * @param array<array-key, TCombineValue>|self<array-key, TCombineValue> $values
345
     *
346
     * @return self<array-key, TCombineValue>
347
     */
348
    public function combine(array|self $values): self
4✔
349
    {
350
        $values = $values instanceof self
4✔
351
            ? $values->toArray()
1✔
352
            : $values;
3✔
353

354
        return new self(array_combine($this->array, $values));
4✔
355
    }
356

357
    /**
358
     * Creates an array from the specified `$string`, split by the given `$separator`.
359
     */
360
    public static function explode(string|Stringable $string, string $separator = ' '): self
4✔
361
    {
362
        if ($separator === '') {
4✔
363
            return new self([(string) $string]);
1✔
364
        }
365

366
        return new self(explode($separator, (string) $string));
4✔
367
    }
368

369
    /**
370
     * Asserts whether this instance is equal to the given array.
371
     */
372
    public function equals(array|self $other): bool
14✔
373
    {
374
        $other = is_array($other) ? $other : $other->array;
14✔
375

376
        return $this->array === $other;
14✔
377
    }
378

379
    /**
380
     * Returns the first item in the instance that matches the given `$filter`.
381
     * If `$filter` is `null`, returns the first item.
382
     *
383
     * @param Closure(mixed $value, mixed $key): bool $filter
384
     */
385
    public function first(?Closure $filter = null): mixed
14✔
386
    {
387
        if ($this->array === []) {
14✔
388
            return null;
2✔
389
        }
390

391
        if ($filter === null) {
14✔
392
            return $this->array[array_key_first($this->array)];
2✔
393
        }
394

395
        foreach ($this as $key => $value) {
12✔
396
            if ($filter($value, $key)) {
12✔
397
                return $value;
12✔
398
            }
399
        }
400

401
        return null;
7✔
402
    }
403

404
    /**
405
     * Returns the last item in the instance that matches the given `$filter`.
406
     * If `$filter` is `null`, returns the last item.
407
     *
408
     * @param Closure(mixed $value, mixed $key): bool $filter
409
     */
410
    public function last(?Closure $filter = null): mixed
18✔
411
    {
412
        if ($this->array === []) {
18✔
413
            return null;
2✔
414
        }
415

416
        if ($filter === null) {
18✔
417
            return $this->array[array_key_last($this->array)];
18✔
418
        }
419

UNCOV
420
        foreach ($this->reverse() as $key => $value) {
×
UNCOV
421
            if ($filter($value, $key)) {
×
UNCOV
422
                return $value;
×
423
            }
424
        }
425

UNCOV
426
        return null;
×
427
    }
428

429
    /**
430
     * Returns an instance of the array without the last value.
431
     *
432
     * @param mixed $value The popped value will be stored in this variable
433
     */
434
    public function pop(mixed &$value = null): self
17✔
435
    {
436
        $value = $this->last();
17✔
437

438
        return new self(array_slice($this->array, 0, -1));
17✔
439
    }
440

441
    /**
442
     * Returns an instance of the array without the first value.
443
     *
444
     * @param mixed $value The unshifted value will be stored in this variable
445
     */
446
    public function unshift(mixed &$value = null): self
1✔
447
    {
448
        $value = $this->first();
1✔
449

450
        return new self(array_slice($this->array, 1));
1✔
451
    }
452

453
    /**
454
     * Returns a new instance of the array in reverse order.
455
     */
456
    public function reverse(): self
1✔
457
    {
458
        return new self(array_reverse($this->array));
1✔
459
    }
460

461
    /**
462
     * Asserts whether the array is empty.
463
     */
464
    public function isEmpty(): bool
17✔
465
    {
466
        return empty($this->array);
17✔
467
    }
468

469
    /**
470
     * Asserts whether the array is not empty.
471
     */
472
    public function isNotEmpty(): bool
14✔
473
    {
474
        return ! $this->isEmpty();
14✔
475
    }
476

477
    /**
478
     * Returns an instance of `StringHelper` with the values of the instance joined with the given `$glue`.
479
     */
480
    public function implode(string $glue): StringHelper
79✔
481
    {
482
        return str(implode($glue, $this->array));
79✔
483
    }
484

485
    /**
486
     * Returns a new instance with the keys of this array as values.
487
     *
488
     * @return self<array-key, TKey>
489
     */
490
    public function keys(): self
1✔
491
    {
492
        return new self(array_keys($this->array));
1✔
493
    }
494

495
    /**
496
     * Returns a new instance of this array without its keys.
497
     *
498
     * @return self<int, TValue>
499
     */
500
    public function values(): self
350✔
501
    {
502
        return new self(array_values($this->array));
350✔
503
    }
504

505
    /**
506
     * Returns a new instance of this array with only the items that pass the given `$filter`.
507
     * If `$filter` is `null`, the new instance will contain only values that are not `false` or `null`.
508
     *
509
     * @param null|Closure(mixed $value, mixed $key): bool $filter
510
     */
511
    public function filter(?Closure $filter = null): self
1✔
512
    {
513
        $array = [];
1✔
514
        $filter ??= static fn (mixed $value, mixed $_) => ! in_array($value, [false, null], strict: true);
1✔
515

516
        foreach ($this->array as $key => $value) {
1✔
517
            if ($filter($value, $key)) {
1✔
518
                $array[$key] = $value;
1✔
519
            }
520
        }
521

522
        return new self($array);
1✔
523
    }
524

525
    /**
526
     * Applies the given callback to all items of the instance.
527
     *
528
     * @param Closure(mixed $value, mixed $key): void $each
529
     */
530
    public function each(Closure $each): self
69✔
531
    {
532
        foreach ($this as $key => $value) {
69✔
533
            $each($value, $key);
67✔
534
        }
535

536
        return $this;
69✔
537
    }
538

539
    /**
540
     * Returns a new instance of the array, with each item transformed by the given callback.
541
     *
542
     * @param Closure(mixed $value, mixed $key): mixed $map
543
     */
544
    public function map(Closure $map): self
344✔
545
    {
546
        $array = [];
344✔
547

548
        foreach ($this->array as $key => $value) {
344✔
549
            $array[$key] = $map($value, $key);
344✔
550
        }
551

552
        return new self($array);
344✔
553
    }
554

555
    /**
556
     * Returns a new instance of the array, with each item transformed by the given callback.
557
     * The callback must return a generator, associating a key and a value.
558
     *
559
     * ### Example
560
     * ```php
561
     * arr(['a', 'b'])->mapWithKeys(fn (mixed $value, mixed $key) => yield $key => $value);
562
     * ```
563
     *
564
     * @param Closure(mixed $value, mixed $key): Generator $map
565
     */
566
    public function mapWithKeys(Closure $map): self
3✔
567
    {
568
        $array = [];
3✔
569

570
        foreach ($this->array as $key => $value) {
3✔
571
            $generator = $map($value, $key);
3✔
572

573
            if (! $generator instanceof Generator) {
3✔
574
                throw new InvalidMapWithKeysUsage();
1✔
575
            }
576

577
            $array[$generator->key()] = $generator->current();
2✔
578
        }
579

580
        return new self($array);
2✔
581
    }
582

583
    /**
584
     * Gets the value identified by the specified `$key`, or `$default` if no such value exists.
585
     *
586
     * @return mixed|ArrayHelper
587
     */
588
    public function get(string $key, mixed $default = null): mixed
348✔
589
    {
590
        $value = $this->array;
348✔
591

592
        $keys = explode('.', $key);
348✔
593

594
        foreach ($keys as $key) {
348✔
595
            if (! isset($value[$key])) {
348✔
596
                return $default;
2✔
597
            }
598

599
            $value = $value[$key];
348✔
600
        }
601

602
        if (is_array($value)) {
348✔
603
            return new self($value);
341✔
604
        }
605

606
        return $value;
8✔
607
    }
608

609
    /**
610
     * Asserts whether a value identified by the specified `$key` exists.
611
     */
612
    public function has(string $key): bool
1✔
613
    {
614
        $array = $this->array;
1✔
615

616
        $keys = explode('.', $key);
1✔
617

618
        foreach ($keys as $key) {
1✔
619
            if (! isset($array[$key])) {
1✔
620
                return false;
1✔
621
            }
622

623
            $array = &$array[$key];
1✔
624
        }
625

626
        return true;
1✔
627
    }
628

629
    /**
630
     * Asserts whether the instance contains an item that can be identified by `$search`.
631
     */
632
    public function contains(mixed $search): bool
1✔
633
    {
634
        return $this->first(fn (mixed $value) => $value === $search) !== null;
1✔
635
    }
636

637
    /**
638
     * Associates the given `$value` to the given `$key` on the instance.
639
     */
640
    public function set(string $key, mixed $value): self
2✔
641
    {
642
        $array = $this->array;
2✔
643

644
        $current = &$array;
2✔
645

646
        $keys = explode('.', $key);
2✔
647

648
        foreach ($keys as $i => $key) {
2✔
649
            // If this is the last key in dot notation, we don't
650
            // need to go through the next steps.
651
            if (count($keys) === 1) {
2✔
652
                break;
2✔
653
            }
654

655
            // Remove the current key from our keys array
656
            // so that later we can use the first value
657
            // from that array as our key.
658
            unset($keys[$i]);
2✔
659

660
            // If we know this key is not an array, make it one.
661
            if (! isset($current[$key]) || ! is_array($current[$key])) {
2✔
662
                $current[$key] = [];
2✔
663
            }
664

665
            // Set the context to this key.
666
            $current = &$current[$key];
2✔
667
        }
668

669
        // Pull the first key out of the array
670
        // and use it to set the value.
671
        $current[array_shift($keys)] = $value;
2✔
672

673
        return new self($array);
2✔
674
    }
675

676
    /**
677
     * @alias of `set`
678
     */
679
    public function put(string $key, mixed $value): self
1✔
680
    {
681
        return $this->set($key, $value);
1✔
682
    }
683

684
    /**
685
     * Converts the dot-notated keys of the instance to a set of nested arrays.
686
     */
687
    public function unwrap(): self
91✔
688
    {
689
        $unwrapValue = function (string|int $key, mixed $value) {
91✔
690
            if (is_int($key)) {
87✔
UNCOV
691
                return [$key => $value];
×
692
            }
693

694
            $keys = explode('.', $key);
87✔
695

696
            for ($i = array_key_last($keys); $i >= 0; $i--) {
87✔
697
                $currentKey = $keys[$i];
87✔
698

699
                $value = [$currentKey => $value];
87✔
700
            }
701

702
            return $value;
87✔
703
        };
91✔
704

705
        $array = [];
91✔
706

707
        foreach ($this->array as $key => $value) {
91✔
708
            $array = array_merge_recursive($array, $unwrapValue($key, $value));
87✔
709
        }
710

711
        return new self($array);
91✔
712
    }
713

714
    /**
715
     * Joins all values using the specified `$glue`. The last item of the string is separated by `$finalGlue`.
716
     */
717
    public function join(string $glue = ', ', ?string $finalGlue = ' and '): StringHelper
16✔
718
    {
719
        if ($finalGlue === '' || is_null($finalGlue)) {
16✔
720
            return $this->implode($glue);
2✔
721
        }
722

723
        if ($this->isEmpty()) {
14✔
UNCOV
724
            return str('');
×
725
        }
726

727
        $parts = $this->pop($last);
14✔
728

729
        if ($parts->isNotEmpty()) {
14✔
730
            return $parts->implode($glue)->append($finalGlue, $last);
9✔
731
        }
732

733
        return str($last);
6✔
734
    }
735

736
    /**
737
     * Dumps the instance.
738
     */
UNCOV
739
    public function dump(mixed ...$dumps): self
×
740
    {
741
        lw($this->array, ...$dumps);
×
742

743
        return $this;
×
744
    }
745

746
    /**
747
     * Dumps the instance and stops the execution of the script.
748
     */
UNCOV
749
    public function dd(mixed ...$dd): void
×
750
    {
UNCOV
751
        ld($this->array, ...$dd);
×
752
    }
753

754
    /**
755
     * Returns the underlying array of the instance.
756
     *
757
     * @return array<TKey, TValue>
758
     */
759
    public function toArray(): array
383✔
760
    {
761
        return $this->array;
383✔
762
    }
763

764
    /**
765
     * Maps the items of the instance to the given object.
766
     *
767
     * @see Tempest\map()
768
     *
769
     * @template T
770
     * @param class-string<T> $to
771
     * @return self<T>
772
     */
773
    public function mapTo(string $to): self
1✔
774
    {
775
        return new self(map($this->array)->collection()->to($to));
1✔
776
    }
777

778
    /**
779
     * Returns a new instance of this array sorted by its values.
780
     *
781
     * @param bool $desc Sorts in descending order if `true`; defaults to `false` (ascending).
782
     * @param bool|null $preserveKeys Preserves array keys if `true`; reindexes numerically if `false`.
783
     *                                Defaults to `null`, which auto-detects preservation based on array type  (associative or list).
784
     * @param int $flags Sorting flags to define comparison behavior, defaulting to `SORT_REGULAR`.
785
     * @return self<array-key, TValue> Key type depends on whether array keys are preserved or not.
786
     */
787
    public function sort(bool $desc = false, ?bool $preserveKeys = null, int $flags = SORT_REGULAR): self
10✔
788
    {
789
        $array = $this->array;
10✔
790

791
        if ($preserveKeys === null) {
10✔
792
            $preserveKeys = $this->isAssoc();
10✔
793
        }
794

795
        if ($preserveKeys) {
10✔
796
            $desc ? arsort($array, $flags) : asort($array, $flags);
1✔
797
        } else {
798
            $desc ? rsort($array, $flags) : sort($array, $flags);
10✔
799
        }
800

801
        return new self($array);
10✔
802
    }
803

804
    /**
805
     * Returns a new instance of this array sorted by its values using a callback function.
806
     *
807
     * @param callable $callback The function to use for comparing values. It should accept two parameters
808
     *                           and return an integer less than, equal to, or greater than zero if the
809
     *                           first argument is considered to be respectively less than, equal to, or
810
     *                           greater than the second.
811
     * @param bool|null $preserveKeys Preserves array keys if `true`; reindexes numerically if `false`.
812
     *                                Defaults to `null`, which auto-detects preservation based on array type  (associative or list).
813
     * @return self<array-key, TValue> Key type depends on whether array keys are preserved or not.
814
     */
815
    public function sortByCallback(callable $callback, ?bool $preserveKeys = null): self
1✔
816
    {
817
        $array = $this->array;
1✔
818

819
        if ($preserveKeys === null) {
1✔
820
            $preserveKeys = $this->isAssoc();
1✔
821
        }
822

823
        $preserveKeys ? uasort($array, $callback) : usort($array, $callback);
1✔
824

825
        return new self($array);
1✔
826
    }
827

828
    /**
829
     * Returns a new instance of this array sorted by its keys.
830
     *
831
     * @param bool $desc Sorts in descending order if `true`; defaults to `false` (ascending).
832
     * @param int $flags Sorting flags to define comparison behavior, defaulting to `SORT_REGULAR`.
833
     * @return self<TKey, TValue>
834
     */
835
    public function sortKeys(bool $desc = false, int $flags = SORT_REGULAR): self
1✔
836
    {
837
        $array = $this->array;
1✔
838

839
        $desc ? krsort($array, $flags) : ksort($array, $flags);
1✔
840

841
        return new self($array);
1✔
842
    }
843

844
    /**
845
     * Returns a new instance of this array sorted by its keys using a callback function.
846
     *
847
     * @param callable $callback The function to use for comparing keys. It should accept two parameters
848
     *                           and return an integer less than, equal to, or greater than zero if the
849
     *                           first argument is considered to be respectively less than, equal to, or
850
     *                           greater than the second.
851
     * @return self<TKey, TValue>
852
     */
853
    public function sortKeysByCallback(callable $callback): self
1✔
854
    {
855
        $array = $this->array;
1✔
856

857
        uksort($array, $callback);
1✔
858

859
        return new self($array);
1✔
860
    }
861
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc