php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #17307 sorting using usort() does not produce correct result anymore
Submitted: 2002-05-18 08:36 UTC Modified: 2007-04-03 06:58 UTC
Votes:8
Avg. Score:4.6 ± 0.7
Reproduced:8 of 8 (100.0%)
Same Version:2 (25.0%)
Same OS:6 (75.0%)
From: fcartegnie at nordnet dot fr Assigned:
Status: Closed Package: Arrays related
PHP Version: 4.4.5 OS: Linux
Private report: No CVE-ID: None
 [2002-05-18 08:36 UTC] fcartegnie at nordnet dot fr
I used to make sorts with usort and calling the function below. This was working on the last versions but not anymore on the 4.2.1 :/ which outputs strange result.

function sort_function($a, $b)
{
  if ((($a["livre"]/$a["quantite"]) == ($b["livre"]/$b["quantite"])) || $a["nom"] != $b["nom"]) return 0;
  return (($a["livre"]/$a["quantite"]) < ($b["livre"]/$b["quantite"])) ? -1 : 1;
}

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2002-05-18 08:49 UTC] derick@php.net
Please prive a short self containing script (which runs right away after copy&paste).

Derick
 [2002-05-22 08:44 UTC] fcartegnie at nordnet dot fr
Related to Bug?#17307
 [2002-05-22 08:45 UTC] fcartegnie at nordnet dot fr
Huh ... Related to Bug?#17257

 ;)
 [2002-05-22 14:15 UTC] fcartegnie at nordnet dot fr
<?php


/*
   let's define some array elements
   For example, 3 products (A,B,C) with different price (livre)
*/

$elt["nom"]="A"; $elt["livre"]=10;  $elt["quantite"]=1; $ar[]=$elt;
$elt["nom"]="A"; $elt["livre"]=9;   $elt["quantite"]=1; $ar[]=$elt;
$elt["nom"]="A"; $elt["livre"]=7;   $elt["quantite"]=1; $ar[]=$elt;
$elt["nom"]="A"; $elt["livre"]=11;  $elt["quantite"]=1; $ar[]=$elt;
$elt["nom"]="A"; $elt["livre"]=101; $elt["quantite"]=1; $ar[]=$elt;
$elt["nom"]="B"; $elt["livre"]=104; $elt["quantite"]=1; $ar[]=$elt;
$elt["nom"]="C"; $elt["livre"]=101; $elt["quantite"]=1; $ar[]=$elt;

/*
Our sort function only swap 2 values if the name is the same and the first price is higher than second
As we give a 'nom' field sorted alphabetically array, we might get the result : A7
A9
A10
A11
A101
B104
C101
Which is the result in php 4.0.6
But the result un php 4.2.1 is :
C101
B104
A7
A9
A10
A11
A101
*/

function sort_function($a, $b)
{
 if ((($a["livre"]/$a["quantite"]) == ($b["livre"]/$b["quantite"])) || $a["nom"] != $b["nom"]) return 0;
 return (($a["livre"]/$a["quantite"]) < ($b["livre"]/$b["quantite"])) ? -1 : 1; }

/*
This is a 2nd function, which always returns that elements are equal.
So it might not change the array...
If you try this alternate function, you'll see that the array is modified
to :
B104
C101
A101
A11
A9
A7
A10
??????! really strange behaviour
*/

function sort_function_void($a, $b)
{
return 0;
}

echo "<HTML><PRE>UnSorted:\n";

    for ($i=0;$i<sizeof($ar);$i++)
    echo $ar[$i]["nom"].$ar[$i]["livre"]."\n";

usort($ar, sort_function);
//usort($ar, sort_function_void);

echo "Sorted:\n";

    for ($i=0;$i<sizeof($ar);$i++)
    echo $ar[$i]["nom"].$ar[$i]["livre"]."\n";

?>
 [2002-05-31 05:04 UTC] mauf at franzoni dot info
I've hit the sort problem too... I tried to reproduce it in a few way and I have to say that sometimes it seems to work (sort correctly) some not... unfortunately I can only contribute with a complex routine I use to sort site navigation menus (that it's the only one it always fails on V4.2.1 and works happily in 4.0.6).

For the result: on the left side the table shows the tree as is (and is already sorted) - on the right the same tree after sorting (that indeed is not).

<?
 
$d = array(
  array(2, 0, 8, 1, 0, "branch A", "1", "12,8,0,0,0,0,0"),
  array(2, 0, 9, 1, 0, "branch B", "1", "12,9,0,0,0,0,0"),
  array(2, 0, 10, 1, 0, "branch C", "1", "12,10,0,0,0,0,0"),
  array(2, 0, 11, 1, 0, "branch D", "1", "12,11,0,0,0,0,0"),
  array(2, 0, 12, 1, 0, "branch E", "1", "12,12,0,0,0,0,0"),
  array(2, 0, 13, 1, 0, "branch F", "1", "12,13,0,0,0,0,0"),
  array(2, 0, 14, 1, 0, "branch G", "1", "12,14,0,0,0,0,0"),
  array(2, 0, 15, 1, 0, "branch H", "1", "12,15,0,0,0,0,0"),
  array(2, 0, 16, 1, 0, "branch I", "1", "12,16,0,0,0,0,0"),
  array(2, 0, 17, 1, 0, "branch J", "1", "12,17,0,0,0,0,0"),
  array(2, 0, 18, 1, 0, "branch K", "1", "12,18,0,0,0,0,0"),
  array(2, 0, 19, 1, 0, "branch L", "1", "12,19,0,0,0,0,0"),
  array(2, 0, 20, 1, 0, "branch M", "1", "12,20,0,0,0,0,0"),
  array(2, 0, 21, 1, 0, "branch N", "1", "12,21,0,0,0,0,0"),
  array(2, 0, 155, 1, 0, "branch O", "1", "12,155,0,0,0,0,0"),
  array(2, 0, 163, 1, 0, "branch P", "1", "12,163,0,0,0,0,0"),
  array(2, 0, 981, 1, 0, "branch Q", "1b", "12,981,0,0,0,0,0"),
  array(3, 0, 982, 981, 0, "under branch Q", "2", "12,981,982,0,0,0,0"),
  array(3, 0, 983, 981, 0, "under branch Q too", "2", "12,981,983,0,0,0,0")
);

function sort_level($a, $b) {
// sort navigation array
// indexes : [0] level, [1] order, [2] tree_id, [3] parent_id, [4] parent_ord
// (unused)  [5] descr, [6] class, [7] navigation
 
  if ($a[0] == 2) {           // a at second level
 
    if ($b[0] == 2) {         // both a and b at second level
 
      if ($a[1] == $b[1]) {   // a and b at same level, same order
        if ($a[2] == $b[2]) { // same level, same order, same tree_id (???)
          return 0;
        } else {              // return according to tree_id
          return ($a[2] > $b[2]) ? 1 : -1;
        }
      } else {                // return according to order (in same level)
        return ($a[1] > $b[1]) ? 1 : -1;
      }
 
    } else {                  // a at second level, b at third

      if ($a[2] == $b[3]) {   // b is child of a
        return -1;
      } else {                // return according to a and b's parent order
        return ($a[1] > $b[4]) ? 1 : -1;
      }
    }
 
  } else {                    // a at third level
 
    if ($b[0] == 3) {         // both a and b at third level
 
      if ($a[1] == $b[1]) {   // a and b at same level, same order
        if ($a[2] == $b[2]) { // same level, same order, same tree_id (???)
          return 0;
        } else {              // return according to tree_id
          return ($a[2] > $b[2]) ? 1 : -1;
        }
      } else {                // return according to order (in same level)
        return ($a[1] > $b[1]) ? 1 : -1;
      }
 
    } else {                  // b at second level, a at third
      if ($a[3] == $b[2]) {   // a is child of b
        return 1;
      } else {                // return according to a's parent and b order
        return ($a[4] > $b[1]) ? 1 : -1;
      }
 
    }
 
  }
 
  // just can't get here !
}
 
function dotable($t) {
  print "<table border=1 cellspacing=1 cellpadding=5>\n";
  for ($i = 0; $i < count($t); $i++) {
    print "  <tr>";
    for ($j = 0; $j < count($t[$i]); $j++) {
      print "    <td>".$t[$i][$j]."</td>\n";
    }
    print "  </tr>\n";
  }
  print "</table>\n";
  return;
}
 
?>
<html>
<head>
</head>
<body>
<table border=1 cellspacing=1 cellpadding=5>
<tr>
 <td><? dotable($d); ?></td>
 <? usort($d, "sort_level"); ?>
 <td><? dotable($d); ?></td>
</tr>
</table>
</body>
</html>
 [2002-05-31 05:24 UTC] mfischer@php.net
Please try latest snapshot from snaps.php.net , the problem is supposed to be fixed there.
 [2002-06-04 10:30 UTC] mauf at franzoni dot info
mmmhhh... I just tried php4-200206040600.tar.gz from snaps but it keeps sorting wrong...
 [2002-06-04 10:35 UTC] mfischer@php.net
Setting to critical without further investigation, hope someone can check this out soon.

Btw, are you on a 64bit platform?
 [2002-06-04 12:00 UTC] mauf at franzoni dot info
No, I hit the problem on a P3 first... currently testing on a celeron - both using apache 2.0.36, debian with 2.4.18 on p3, rh7.2 (2.4.7-10) on celeron.

here is the config, if it can helps:

./configure \
  --prefix=/usr/local/php4-200206040600 \
  --with-mysql=/usr/local/mysql-3.23.49 \
  --with-apxs2=/usr/local/httpd-2.0.36/bin/apxs \
  --enable-track-vars \
  --enable-sigchild \
  --enable-shared \
  --enable-cxx \
  --enable-cli=yes

(I tried to track it a while but I get scrambled when it comes into the zend modules :-( )
 [2002-06-04 13:44 UTC] mauf at franzoni dot info
I fixed the problem in my sort routine... it works now (for 4.2.1, so far it seems it was 4.0.6 the buggy one as it has worked for a long while).  So far there may be at most a backward compatibility issue (I still have to check back in 4.0.6) but it is no longer worth the critical status for sure...

sorry for the bothering,
cheers, mauro
 [2002-06-04 13:58 UTC] mfischer@php.net
Heh, ok. Still better then a bug in PHP :) thx.
 [2002-06-17 06:52 UTC] timebreaker at newmail dot ru
Hi!

It's really a problem. I tried to write simple ascending sort routine and got strange results too. Met the conditions at Intel P-133/Win98/Apache2.0.35/PPH4.2.0 and PHP4.2.1, it doesn't matter what the version is. In 4.0.6/4.1.x everything works...
 [2002-06-26 08:12 UTC] david dot derr at milesmedia dot com
Hi all.  I think this is still a bug as I can partially recreate the behaviour that fcartegnie@nordnet.fr reported.

On a script that uses a function passed to usort that always returns 0 (implying equal values), I am seeing that the order of elements in the array is being modified.  The only reason I consider this a bug is because that was not the case in the earlier version I tested.  Here are the details:

-The code I used on both systems:
<?
//The function usort will use.  Returns 0 implying that every comparison is equal.
function fake_sort($left, $right){
        return 0;
}

//create our aTest array and add some data to it
$aTest = array();
$aTest[] = "E";
$aTest[] = "C";
$aTest[] = "B";
$aTest[] = "D";
$aTest[] = "A";

//output the order of elements in the array before running usort on it.
reset($aTest);

echo "Before usort()<br>\n";
while(list($key, $val) = each($aTest)){
        echo "Value: ".$val."<br>\n";
}

//sort it
usort($aTest, "fake_sort");

//output the order of elements in the array after running usort on it.
reset($aTest);

echo "<p>After usort()<br>\n";
while(list($key, $val) = each($aTest)){
        echo "Value: ".$val."<br>\n";
}
?>


-Incorrect (unexpected) Results

System:
RH 7.2, Apache 1.3.26, PHP 4.2.1

Output:
Before usort()
Value: E
Value: C
Value: B
Value: D
Value: A

After usort()
Value: A
Value: D
Value: B
Value: C
Value: E


-Correct (expected) Results

System:
RH 7.2, Apache 1.3.26, PHP 4.1.2

Output:
Before usort()
Value: E
Value: C
Value: B
Value: D
Value: A

After usort()
Value: E
Value: C
Value: B
Value: D
Value: A
 [2007-02-19 18:50 UTC] fcartegnie at nordnet dot fr
Still broken on 4.4.5...
Still using homemade usort routine for 4 years now.
The usort functions really needs to be fixed !

Before usort()<br>
Value: E<br>
Value: C<br>
Value: B<br>
Value: D<br>
Value: A<br>
<p>After usort()<br>
Value: A<br>
Value: D<br>
Value: B<br>
Value: C<br>
Value: E<br>
 [2007-04-03 06:58 UTC] joey@php.net
Thank you for taking the time to write to us, but this is not
a bug. Please double-check the documentation available at
http://www.php.net/manual/ and the instructions on how to report
a bug at http://bugs.php.net/how-to-report.php

As is documented at php.net/usort:

Note: If two members compare as equal, their order in the sorted array
is undefined. Up to PHP 4.0.6 the user defined functions would keep
the original order for those elements, but with the new sort algorithm
introduced with 4.1.0 this is no longer the case as there is no
solution to do so in an efficient way.

 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue May 07 23:01:35 2024 UTC