7
7
use Closure ;
8
8
use InvalidArgumentException ;
9
9
10
- use function array_fill ;
11
10
use function array_multisort ;
12
11
use function count ;
13
12
use function is_array ;
14
- use function is_scalar ;
15
- use function range ;
16
13
17
14
final class ArraySorter
18
15
{
@@ -30,7 +27,7 @@ final class ArraySorter
30
27
* ArraySorter::multisort($data, ['age', 'name'], [SORT_ASC, SORT_DESC]);
31
28
* ```
32
29
*
33
- * After sorting we'll get the following in `$data`:
30
+ * After sorting, we'll get the following in `$data`:
34
31
*
35
32
* ```php
36
33
* [
@@ -43,7 +40,7 @@ final class ArraySorter
43
40
* @param array<array-key, array|object> $array The array to be sorted. The array will be modified after calling
44
41
* this method.
45
42
* @param array<array-key, Closure|string>|Closure|string $key The key(s) to be sorted by. This refers to a key
46
- * name of the sub-array elements, a property name of the objects, or an anonymous function returning the values
43
+ * name of the subarray elements, a property name of the objects, or an anonymous function returning the values
47
44
* for comparison purpose. The anonymous function signature should be: `function($item)`.
48
45
* To sort by multiple keys, provide an array of keys here.
49
46
* @param array<array-key, int>|int $direction The sorting direction. It can be either `SORT_ASC` or `SORT_DESC`.
@@ -62,80 +59,47 @@ public static function multisort(
62
59
array |int $ direction = SORT_ASC ,
63
60
array |int $ sortFlag = SORT_REGULAR
64
61
): void {
65
- $ keys = self :: getKeys ($ array, $ key );
66
- if (empty ( $ keys ) ) {
62
+ $ count = count ($ array );
63
+ if ($ count === 0 ) {
67
64
return ;
68
65
}
69
66
70
- $ n = count ($ keys );
71
- if (is_scalar ($ direction )) {
72
- $ direction = array_fill (0 , $ n , $ direction );
73
- } elseif (count ($ direction ) !== $ n ) {
74
- throw new InvalidArgumentException ('The length of $direction parameter must be the same as that of $keys. ' );
67
+ $ keys = is_array ($ key ) ? $ key : [$ key ];
68
+ $ keysCount = count ($ keys );
69
+ if ($ keysCount === 0 ) {
70
+ return ;
75
71
}
76
72
77
- if (is_scalar ($ sortFlag )) {
78
- $ sortFlag = array_fill (0 , $ n , $ sortFlag );
79
- } elseif (count ($ sortFlag ) !== $ n ) {
73
+ if (is_array ($ direction ) && count ($ direction ) !== $ keysCount ) {
74
+ throw new InvalidArgumentException ('The length of $direction parameter must be the same as that of $keys. ' );
75
+ }
76
+ if (is_array ($ sortFlag ) && count ($ sortFlag ) !== $ keysCount ) {
80
77
throw new InvalidArgumentException ('The length of $sortFlag parameter must be the same as that of $keys. ' );
81
78
}
82
79
83
- $ _args = self ::getArguments ($ array , $ keys , $ direction , $ sortFlag );
84
-
85
- /** @psalm-suppress UnsupportedReferenceUsage */
86
- $ _args [] = &$ array ;
87
-
88
- /** @psalm-suppress MixedArgument */
89
- array_multisort (...$ _args );
90
- }
80
+ $ args = [];
91
81
92
- /**
93
- * Get keys for get arguments.
94
- *
95
- * @param array<array-key, array|object> $array The array to be sorted.
96
- * @param array<array-key, Closure|string>|Closure|string $key The keys to be sorted by. This refers to a key name
97
- * of the sub-array elements, a property name of the objects, or an anonymous function returning the values for
98
- * comparison purpose. The anonymous function signature should be: `function($item)`.
99
- * To sort by multiple keys, provide an array of keys here.
100
- *
101
- * @return array<array-key, Closure|string> The keys.
102
- */
103
- private static function getKeys (array $ array , array |Closure |string $ key ): array
104
- {
105
- $ keys = is_array ($ key ) ? $ key : [$ key ];
106
- if (empty ($ keys ) || empty ($ array )) {
107
- return [];
82
+ for ($ i = 0 ; $ i < $ keysCount ; $ i ++) {
83
+ $ args [] = ArrayHelper::getColumn ($ array , $ keys [$ i ]);
84
+ $ args [] = is_array ($ direction ) ? $ direction [$ i ] : $ direction ;
85
+ $ args [] = is_array ($ sortFlag ) ? $ sortFlag [$ i ] : $ sortFlag ;
108
86
}
109
87
110
- return $ keys ;
111
- }
112
-
113
- /**
114
- * Get arguments for multisort.
115
- *
116
- * @param array<array-key, array|object> $array The array to be sorted.
117
- * @param array<array-key, Closure|string> $keys Array of keys.
118
- * @param array<array-key, int> $direction Array of sorting directions.
119
- * @param array<array-key, int> $sortFlags Array of sort flags.
120
- *
121
- * @return array The arguments.
122
- */
123
- private static function getArguments (array $ array , array $ keys , array $ direction , array $ sortFlags ): array
124
- {
125
- $ args = [];
126
- foreach ($ keys as $ i => $ iKey ) {
127
- $ flag = $ sortFlags [$ i ];
128
- $ args [] = ArrayHelper::getColumn ($ array , $ iKey );
129
- $ args [] = $ direction [$ i ];
130
- $ args [] = $ flag ;
88
+ // Add tie-breaker only for non-empty arrays
89
+ if ($ count > 0 ) {
90
+ $ tieBreaker = [];
91
+ for ($ i = 0 ; $ i < $ count ; $ i ++) {
92
+ $ tieBreaker [$ i ] = $ i + 1 ;
93
+ }
94
+ $ args [] = $ tieBreaker ;
95
+ $ args [] = SORT_ASC ;
96
+ $ args [] = SORT_NUMERIC ;
131
97
}
132
98
133
- // This fix is used for cases when main sorting specified by columns has equal values.
134
- // Without it will lead to Fatal Error: Nesting level too deep - recursive dependency?
135
- $ args [] = range (1 , count ($ array ));
136
- $ args [] = SORT_ASC ;
137
- $ args [] = SORT_NUMERIC ;
99
+ /** @psalm-suppress UnsupportedReferenceUsage */
100
+ $ args [] = &$ array ;
138
101
139
- return $ args ;
102
+ /** @psalm-suppress ArgumentTypeCoercion */
103
+ array_multisort (...$ args );
140
104
}
141
105
}
0 commit comments