Skip to content

Commit cd1aed8

Browse files
DrDetcmb69
authored andcommitted
Fix GH-9296: ksort behaves incorrectly on arrays with mixed keys
The comparator function used at ksort in SORT_REGULAR mode need to be consistent with basic comparison rules. These rules were changed in PHP-8.0 for numeric strings, but comparator used at ksort kept the old behaviour. It leads to inconsistent situations, when after ksort the first key is GREATER than some of the next ones by according to the basic comparison operators. Closes GH-9293.
1 parent 962d8bd commit cd1aed8

File tree

8 files changed

+56
-52
lines changed

8 files changed

+56
-52
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ PHP NEWS
2323
- Standard:
2424
. Fixed bug GH-9017 (php_stream_sock_open_from_socket could return NULL).
2525
(Heiko Weber)
26+
. Fixed bug GH-9296 (`ksort` behaves incorrectly on arrays with mixed keys).
27+
(Denis Vaksman)
2628

2729
- Streams:
2830
. Fixed bug GH-8472 (The resource returned by stream_socket_accept may have

ext/standard/array.c

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -166,40 +166,25 @@ static zend_never_inline ZEND_COLD int stable_sort_fallback(Bucket *a, Bucket *b
166166

167167
static zend_always_inline int php_array_key_compare_unstable_i(Bucket *f, Bucket *s) /* {{{ */
168168
{
169-
zend_uchar t;
170-
zend_long l1, l2;
171-
double d;
172-
173-
if (f->key == NULL) {
174-
if (s->key == NULL) {
175-
return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
176-
} else {
177-
l1 = (zend_long)f->h;
178-
t = is_numeric_string(s->key->val, s->key->len, &l2, &d, 1);
179-
if (t == IS_LONG) {
180-
/* pass */
181-
} else if (t == IS_DOUBLE) {
182-
return ZEND_NORMALIZE_BOOL((double)l1 - d);
183-
} else {
184-
l2 = 0;
185-
}
186-
}
187-
} else {
188-
if (s->key) {
189-
return zendi_smart_strcmp(f->key, s->key);
190-
} else {
191-
l2 = (zend_long)s->h;
192-
t = is_numeric_string(f->key->val, f->key->len, &l1, &d, 1);
193-
if (t == IS_LONG) {
194-
/* pass */
195-
} else if (t == IS_DOUBLE) {
196-
return ZEND_NORMALIZE_BOOL(d - (double)l2);
197-
} else {
198-
l1 = 0;
199-
}
200-
}
201-
}
202-
return ZEND_NORMALIZE_BOOL(l1 - l2);
169+
zval first;
170+
zval second;
171+
172+
if (f->key == NULL && s->key == NULL) {
173+
return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
174+
} else if (f->key && s->key) {
175+
return zendi_smart_strcmp(f->key, s->key);
176+
}
177+
if (f->key) {
178+
ZVAL_STR(&first, f->key);
179+
} else {
180+
ZVAL_LONG(&first, f->h);
181+
}
182+
if (s->key) {
183+
ZVAL_STR(&second, s->key);
184+
} else {
185+
ZVAL_LONG(&second, s->h);
186+
}
187+
return zend_compare(&first, &second);
203188
}
204189
/* }}} */
205190

ext/standard/tests/array/002.phpt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,8 @@ array(8) {
276276
-- Testing krsort() --
277277
No second argument:
278278
array(8) {
279+
["test"]=>
280+
int(27)
279281
[16777216]=>
280282
float(-0.3333333333333333)
281283
[1001]=>
@@ -288,8 +290,6 @@ array(8) {
288290
string(4) "Test"
289291
[0]=>
290292
string(3) "PHP"
291-
["test"]=>
292-
int(27)
293293
[-1000]=>
294294
array(2) {
295295
[0]=>
@@ -300,6 +300,8 @@ array(8) {
300300
}
301301
Using SORT_REGULAR:
302302
array(8) {
303+
["test"]=>
304+
int(27)
303305
[16777216]=>
304306
float(-0.3333333333333333)
305307
[1001]=>
@@ -312,8 +314,6 @@ array(8) {
312314
string(4) "Test"
313315
[0]=>
314316
string(3) "PHP"
315-
["test"]=>
316-
int(27)
317317
[-1000]=>
318318
array(2) {
319319
[0]=>
@@ -334,10 +334,10 @@ array(8) {
334334
string(27) "PHP: Hypertext Preprocessor"
335335
[5]=>
336336
string(4) "Test"
337-
[0]=>
338-
string(3) "PHP"
339337
["test"]=>
340338
int(27)
339+
[0]=>
340+
string(3) "PHP"
341341
[-1000]=>
342342
array(2) {
343343
[0]=>
@@ -383,8 +383,6 @@ array(8) {
383383
}
384384
[0]=>
385385
string(3) "PHP"
386-
["test"]=>
387-
int(27)
388386
[5]=>
389387
string(4) "Test"
390388
[17]=>
@@ -395,6 +393,8 @@ array(8) {
395393
string(6) "monkey"
396394
[16777216]=>
397395
float(-0.3333333333333333)
396+
["test"]=>
397+
int(27)
398398
}
399399
Using SORT_REGULAR:
400400
array(8) {
@@ -407,8 +407,6 @@ array(8) {
407407
}
408408
[0]=>
409409
string(3) "PHP"
410-
["test"]=>
411-
int(27)
412410
[5]=>
413411
string(4) "Test"
414412
[17]=>
@@ -419,6 +417,8 @@ array(8) {
419417
string(6) "monkey"
420418
[16777216]=>
421419
float(-0.3333333333333333)
420+
["test"]=>
421+
int(27)
422422
}
423423
Using SORT_NUMERIC:
424424
array(8) {

ext/standard/tests/array/gh9296.phpt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
GH-9296: incorrect ksort(..., SORT_REGULAR) behaviour on arrays with numeric and string keys
3+
--FILE--
4+
<?php
5+
$array = ["aaa" => 0, 600 => 1];
6+
ksort($array, SORT_REGULAR);
7+
var_dump($array);
8+
var_dump(array_key_first($array) <=> array_key_last($array));
9+
?>
10+
--EXPECT--
11+
array(2) {
12+
[600]=>
13+
int(1)
14+
["aaa"]=>
15+
int(0)
16+
}
17+
int(-1)
0 Bytes
Binary file not shown.

ext/standard/tests/array/krsort_variation9.phpt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,22 +82,22 @@ array(5) {
8282
- With default sort flag -
8383
bool(true)
8484
array(3) {
85+
["c"]=>
86+
string(5) "apple"
8587
["a"]=>
8688
string(6) "orange"
8789
[0]=>
8890
string(6) "banana"
89-
["c"]=>
90-
string(5) "apple"
9191
}
9292
- Sort flag = SORT_REGULAR -
9393
bool(true)
9494
array(3) {
95+
["c"]=>
96+
string(5) "apple"
9597
["a"]=>
9698
string(6) "orange"
9799
[0]=>
98100
string(6) "banana"
99-
["c"]=>
100-
string(5) "apple"
101101
}
102102

103103
-- Iteration 3 --
0 Bytes
Binary file not shown.

ext/standard/tests/array/ksort_variation9.phpt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,20 +81,20 @@ array(5) {
8181
- With default sort flag -
8282
bool(true)
8383
array(3) {
84-
["a"]=>
85-
string(6) "orange"
8684
[0]=>
8785
string(6) "banana"
86+
["a"]=>
87+
string(6) "orange"
8888
["c"]=>
8989
string(5) "apple"
9090
}
9191
- Sort flag = SORT_REGULAR -
9292
bool(true)
9393
array(3) {
94-
["a"]=>
95-
string(6) "orange"
9694
[0]=>
9795
string(6) "banana"
96+
["a"]=>
97+
string(6) "orange"
9898
["c"]=>
9999
string(5) "apple"
100100
}

0 commit comments

Comments
 (0)