Bug #69158 [Nab]: Inconsistency: usort does not return the same result on PHP < 7 and PHP7

From: Date: Mon, 07 Sep 2015 07:39:12 +0000
Subject: Bug #69158 [Nab]: Inconsistency: usort does not return the same result on PHP < 7 and PHP7
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-195825@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=69158&edit=1

 ID:                 69158
 Updated by:         requinix@php.net
 Reported by:        contact at jubianchi dot fr
 Summary:            Inconsistency: usort does not return the same result
                     on PHP < 7 and PHP7
 Status:             Not a bug
 Type:               Bug
 Package:            Arrays related
 PHP Version:        master-Git-2015-03-02 (Git)
 Block user comment: N
 Private report:     N

 New Comment:

@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


Previous Comments:
------------------------------------------------------------------------
[2015-09-07 06:30:54] 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-03-02 18:50:13] 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-03-02 14:49:00] 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"
  }
}


------------------------------------------------------------------------



--
Edit this bug report at https://bugs.php.net/bug.php?id=69158&edit=1


Thread (6 messages)

« previous php.bugs (#195825) next »