Bug #71238 [Opn->Csd]: usort pass by reference inconsistency
| From: | nikic@php.net | Date: | Thu, 15 Jul 2021 14:04:57 +0000 |
| Subject: | Bug #71238 [Opn->Csd]: usort pass by reference inconsistency | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-235058@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=71238&edit=1
ID: 71238
Updated by: nikic@php.net
Reported by: eugen dot alter at gmail dot com
Summary: usort pass by reference inconsistency
-Status: Open
+Status: Closed
Type: Bug
Package: Arrays related
Operating System: any
PHP Version: 7.0.1
-Assigned To:
+Assigned To: nikic
Block user comment: N
Private report: N
New Comment:
As of PHP 8.0 this will throw warnings along the lines of:
Warning: {closure}(): Argument #1 ($x) must be passed by reference, value given in /in/UfuCq on line
37
Warning: {closure}(): Argument #2 ($y) must be passed by reference, value given in /in/UfuCq on line
37
Which should make it clear that references are intentionally not supported here, rather than just
silently ignoring them.
Previous Comments:
------------------------------------------------------------------------
[2016-01-03 14:46:16] white06tiger+PHPbug at gmail dot com
I have the same issue with
uksort() and WITHOUT modifying the actual array.
This bug seems to trigger as soon as anything accesses the array to be sorted.
Following code is supposed to sort an array by values and key (in this case, behaves like a stable
sort)
```
<?php
error_reporting(-1);
header('Content-Type: text/plain');
function uasort_key(&$arr, $func) {
return uksort($arr, function($a,$b) use(&$arr,$func) {
return $func($arr[$a], $arr[$b], $a, $b);
});
}
function sort_me($a, $b, $ka = 0, $kb = 0) {
return ($a < $b ? -1 : ($a > $b ? 1 :
($ka < $kb ? -1 : ($ka > $kb ? 1 : 0))
));
}
$test_array = array(4,6,2,1,7,5,1,34,1,0,4);
$test_array_uasort = $test_array;
$test_array_uasort_key = $test_array;
uasort($test_array_uasort, 'sort_me');
uasort_key($test_array_uasort_key, 'sort_me');
echo 'PHP: ', phpversion(), "\n\n";
echo 'Unsorted: ';
print_r($test_array);
echo 'uasort: ';
print_r($test_array_uasort);
echo 'uasort_key: ';
print_r($test_array_uasort_key);
echo "\n\n", 'Source: ', "\n\n";
readfile(__FILE__);
?>
```
In PHP 5.3 you'll get the false-positive warning:
âWarning: uksort(): Array was modified by the user comparison function in sort_bug.php on line
8â which got seemingly fixed in bug #50688 for PHP 7.. Maybe said "fix" introduced
our current faulty behavior.
Expected result (PHP 5.3):
-----
uasort_key: Array
(
[9] => 0
[3] => 1
[6] => 1
[8] => 1
[2] => 2
[0] => 4
[10] => 4
[5] => 5
[1] => 6
[4] => 7
[7] => 34
)
-----
Actual result (PHP 7.0.1):
-----
uasort_key: Array
(
[9] => 0
[6] => 1
[3] => 1
[2] => 2
[0] => 4
[1] => 6
[10] => 4
[8] => 1
[5] => 5
[4] => 7
[7] => 34
)
-----
------------------------------------------------------------------------
[2015-12-29 18:24:50] levim@php.net
Using var_export in PHP 5.5:
array (
0 =>
array (
'name' => 'Bar',
'children' =>
array (
0 =>
array (
'name' => 'X',
'children' =>
array (
),
),
1 =>
array (
'name' => 'Y',
'children' =>
array (
),
),
2 =>
array (
'name' => 'Z',
'children' =>
array (
),
),
),
),
1 =>
array (
'name' => 'Foo',
'children' =>
array (
0 =>
array (
'name' => 'A',
'children' =>
array (
),
),
1 =>
array (
'name' => 'B',
'children' =>
array (
),
),
),
),
)
Using var_export in a slightly outdated master branch:
array (
0 =>
array (
'name' => 'Bar',
'children' =>
array (
0 =>
array (
'name' => 'Z',
'children' =>
array (
),
),
1 =>
array (
'name' => 'X',
'children' =>
array (
),
),
2 =>
array (
'name' => 'Y',
'children' =>
array (
),
),
),
),
1 =>
array (
'name' => 'Foo',
'children' =>
array (
0 =>
array (
'name' => 'B',
'children' =>
array (
),
),
1 =>
array (
'name' => 'A',
'children' =>
array (
),
),
),
),
)
------------------------------------------------------------------------
[2015-12-29 16:38:59] eugen dot alter at gmail dot com
Description:
------------
In PHP 5.4 ... 5.6 passing compared array elements by reference and then changing them from inside
comparison function works properly whilst in PHP7 it does not.
In sample script: function tries to sort arrays by key "name" on all nested levels. The
nested levels are sorted correctly on PHP 5.4 .. 5.6 but not on PHP7.
Test script:
---------------
$array = [
[
'name' => 'Foo',
'children' => [
[
'name' => 'B',
'children' => []
],
[
'name' => 'A',
'children' => []
]
]
],
[
'name' => 'Bar',
'children' => [
[
'name' => 'Z',
'children' => []
],
[
'name' => 'X',
'children' => []
],
[
'name' => 'Y',
'children' => []
]
]
]
];
usort($array, $f = function(&$x, &$y) use (&$f){
usort($x['children'], $f);
usort($y['children'], $f);
return strcasecmp($x['name'], $y['name']);
});
Expected result:
----------------
array(2) { [0]=> array(2) { ["name"]=> string(3) "Bar"
["children"]=> array(3) { [0]=> array(2) { ["name"]=> string(1)
"X" ["children"]=> array(0) { } } [1]=> array(2) { ["name"]=>
string(1) "Y" ["children"]=> array(0) { } } [2]=> array(2) {
["name"]=> string(1) "Z" ["children"]=> array(0) { } } } }
[1]=> array(2) { ["name"]=> string(3) "Foo" ["children"]=>
array(2) { [0]=> array(2) { ["name"]=> string(1) "A"
["children"]=> array(0) { } } [1]=> array(2) { ["name"]=> string(1)
"B" ["children"]=> array(0) { } } } } }
Actual result:
--------------
array(2) { [0]=> array(2) { ["name"]=> string(3) "Bar"
["children"]=> array(3) { [0]=> array(2) { ["name"]=> string(1)
"Z" ["children"]=> array(0) { } } [1]=> array(2) { ["name"]=>
string(1) "X" ["children"]=> array(0) { } } [2]=> array(2) {
["name"]=> string(1) "Y" ["children"]=> array(0) { } } } }
[1]=> array(2) { ["name"]=> string(3) "Foo" ["children"]=>
array(2) { [0]=> array(2) { ["name"]=> string(1) "B"
["children"]=> array(0) { } } [1]=> array(2) { ["name"]=> string(1)
"A" ["children"]=> array(0) { } } } } }
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=71238&edit=1