php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #71238 usort pass by reference inconsistency
Submitted: 2015-12-29 16:38 UTC Modified: -
Votes:8
Avg. Score:4.4 ± 1.3
Reproduced:6 of 6 (100.0%)
Same Version:4 (66.7%)
Same OS:6 (100.0%)
From: eugen dot alter at gmail dot com Assigned:
Status: Open Package: Arrays related
PHP Version: 7.0.1 OS: any
Private report: No CVE-ID: None
View Add Comment Developer Edit
Anyone can comment on a bug. Have a simpler test case? Does it work for you on a different platform? Let us know!
Just going to say 'Me too!'? Don't clutter the database with that please — but make sure to vote on the bug!
Your email address:
MUST BE VALID
Solve the problem:
37 - 32 = ?
Subscribe to this entry?

 
 [2015-12-29 16:38 UTC] 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) { } } } } } 

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-12-29 18:24 UTC] 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 (
        ),
      ),
    ),
  ),
)
 [2016-01-03 14:46 UTC] 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
)
-----
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Sun Aug 18 20:01:27 2019 UTC