php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #69158 Inconsistency: usort does not return the same result on PHP < 7 and PHP7
Submitted: 2015-03-02 14:49 UTC Modified: 2015-09-07 07:39 UTC
From: contact at jubianchi dot fr Assigned:
Status: Not a bug Package: Arrays related
PHP Version: master-Git-2015-03-02 (Git) OS:
Private report: No CVE-ID: None
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 !
Your email address:
MUST BE VALID
Solve the problem:
36 - 27 = ?
Subscribe to this entry?

 
 [2015-03-02 14:49 UTC] contact at jubianchi dot fr
Description:
------------
There is a tiny difference on usort result between php < 7 and php7 (master): items are not ordered the same way between those versions.

PHP7 seems to be consistent with HVVM but not with earlier version of PHP :)

Test script:
---------------
<?php

// http://3v4l.org/RYTt9

$array = array(
    array(
        'file' => 'file1',
        'line' => 1,
        'message' => 'foo'
    ),
    array(
        'file' => 'file1',
        'line' => 1,
        'message' => 'bar'
    )
);

usort($array, function($a, $b) {
        if ($a['file'] !== $b['file'])
        {
            return strcmp($a['file'], $b['file']);
        }
        else if ($a['line'] === $b['line'])
        {
            return 0;
        }
        else
        {
            return ($a['line'] < $b['line'] ? -1 : 1);
        }
    }
);

var_dump($array);

Expected result:
----------------
array(2) {
  [0]=>
  array(3) {
    ["file"]=>
    string(5) "file1"
    ["line"]=>
    int(1)
    ["message"]=>
    string(3) "bar"
  }
  [1]=>
  array(3) {
    ["file"]=>
    string(5) "file1"
    ["line"]=>
    int(1)
    ["message"]=>
    string(3) "foo"
  }
}

Actual result:
--------------
array(2) {
  [0]=>
  array(3) {
    ["file"]=>
    string(5) "file1"
    ["line"]=>
    int(1)
    ["message"]=>
    string(3) "foo"
  }
  [1]=>
  array(3) {
    ["file"]=>
    string(5) "file1"
    ["line"]=>
    int(1)
    ["message"]=>
    string(3) "bar"
  }
}

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-03-02 18:50 UTC] aharvey@php.net
-Status: Open +Status: Not a bug
 [2015-03-02 18:50 UTC] aharvey@php.net
This is expected: PHP's sorting functions aren't guaranteed to be stable ($a and $b are "equal" in this case, since the array callback will return 0 as their file and line elements are identical), and there have been implementation changes in PHP 7 that affect this case.
 [2015-09-07 06:30 UTC] matt at piwik dot org
Hello, 

at Piwik we are building the leading open source alternative to Google Analytics.

This change in PHP7 usort function internals, is causing us some trouble. We need our test suite to run on PHP5 and PHP7 and return the same results. Due to this change in "secondary sort order" (maybe not the best name), it isn't possible for us to have test suites work on both PHP version. We have some few dozens tests failing on PHP7 (test outputs on travis are linked from: https://github.com/piwik/piwik/issues/8689#issuecomment-137424173 )

If you could change usort implementation to keep same behavior as PHP5, it would be awesome and would surely help many people like us have test suites run on both PH5 and PHP7 consistently :-)

Keep up the great work!
 [2015-09-07 07:39 UTC] requinix@php.net
@matt: As @aharvey said, PHP has never guaranteed a stable sort for "equal" items. If your array was defined in a different order, or if you merely added or removed an item from it, then the result could be in a different order.

There is no reason for pos5 to sort before pos3 besides "that's what happened when the author ran this code in PHP 5". Maybe I'm naive but I'd think the test should verify that each item is <= the next item; that is all that sorting guarantees* and assuming more would be a mistake.

The code is a bit too complex for me to suggest an actual solution, but I'm thinking along the lines of:

  $growth = -99;
  for each $value in the column {
    assert that $value['growth'] >= $growth;
    $growth = $value['growth'];
  }

Or you can guarantee a stable sort by doing a final comparison (ie, when all other comparisons returned equality) on a unique, even if arbitrary, value. Perhaps "label" would fit the bill?

* given a consistent comparison function
 [2015-12-21 17:49 UTC] gooomax at gmail dot com
So if at php7 this is not a bug - it is a bug on php < 7, isn't it?
 [2017-06-30 22:12 UTC] jasdeep at sikher dot com
I was having the same problem and managed to get the array ordering with usort correct from PHP 4.x through to PHP 7.x by using the following code:

<?php

// https://3v4l.org/0sTAW

function test($firstValue, $secondValue) {
    if ($firstValue > $secondValue) {
        return 1;
    }
    if ($firstValue < $secondValue) {
        return -1;
    }
    return 0;
}
$array = array('a', 'b', 'a', 'b', 'c');
usort($array, 'test');
echo print_r($array);
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Mon Nov 25 22:01:31 2024 UTC