php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #50688 Using exceptions inside usort() callback function causes a warning
Submitted: 2010-01-07 19:42 UTC Modified: 2017-12-06 18:14 UTC
Votes:411
Avg. Score:4.1 ± 1.0
Reproduced:378 of 383 (98.7%)
Same Version:299 (79.1%)
Same OS:44 (11.6%)
From: jcampbell at remindermedia dot com Assigned: stas (profile)
Status: Closed Package: Arrays related
PHP Version: 5.*, 6 OS: Fedora Core 12
Private report: No CVE-ID: None
 [2010-01-07 19:42 UTC] jcampbell at remindermedia dot com
Description:
------------
If the callback function used by usort handles an exception using a try/catch block, a warning is generated. The correct sorting is still done. This happens even when the exception & handling doesn't involve the variables.

The example below is the usort example from the manual with only the try/catch block added. Reproducible in PHP 5.2.11 but not 5.2.9

Reproduce code:
---------------
<?php
function cmp($a, $b)
{
    if ($a == $b) {
        return 0;
    }

    try {
        throw new Exception();
    } catch (Exception $E) {

    }

    return ($a < $b) ? -1 : 1;
}

$a = array(3, 2, 5, 6, 1);

usort($a, "cmp");


Expected result:
----------------
No warning message.

Actual result:
--------------
PHP Warning:  usort(): Array was modified by the user comparison function in /home/jcampbell/usortExceptionWarning.php on line 19

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2010-01-08 01:51 UTC] federico dot lebron at gmail dot com
The problem seems to be that usort checks the amount of references 
before and after the function call to see if the user-provided function 
modified it, but inside the function call, debug_backtrace_get_args adds 
a reference to the passed variables to use in e.g. debug_backtrace's 
"arg" element.
 [2010-01-20 10:22 UTC] jani@php.net
This was caused by the fix for bug #50006 (there weren't such checks before :)

Stas, can you check this out? Didn't expect anyone to use exceptions, did you? :D
 [2010-03-05 17:41 UTC] bernie at dcbl dot ca
affects gentoo builds after > 5.2.10 (5.2.11, 5.2.11-r1, and 5.2.12)
 [2010-04-01 02:12 UTC] stas@php.net
The reason seems to be that when making exception backtrace, debug_backtrace_get_args() uses SEPARATE_ZVAL_TO_MAKE_IS_REF() on arguments, which makes it look as if the argument was indeed modified (which usort is designed to protect against, since cmp callback is not supposed to modify the arguments)
 [2010-05-31 17:24 UTC] ajrattink at correct dot net
I printed a debug line from my usort callback. It called debug_backtrace() to print the line and sourcefile in the debuglog. And therefor triggered the error. Even more, it did not sort.

Maybe the phpmanual should state that usort() callbacks are not allowed to write loglines. I also think that usort() callbacks that DO change the array are perfectly legal, as long as they don't change the sort.  

Maybe your sorter code needs stackoverflow protection or whatever, but calling certain code 'invalid', because it causes your code to SEGV is a stupid way to solve a bug.
 [2010-10-07 23:34 UTC] philipwhiuk at hotmail dot com
I notice this is still affecting PHP 5.3.3 (Windows/Apache install).

Is this likely to be fixed soon - is it a question of developer time and priority 
or is it too difficult to fix?

It's quite irritating - I realise that the obvious solution is to avoid throwing 
the exception (ha-ha) but it's a useful function and exceptions are... inevitable.
 [2011-10-10 21:44 UTC] poehler at interworx dot com
This bug is still present as of PHP 5.3.8, we ran into it today and spent most of a day trying to figure out what was causing the error message "Array was modified by the user comparison function", when CLEARLY, NOTHING was changing the array at all!

The exception was not thrown/caught directly in the usort function but rather in a constructor of a class that was called about 3 or 4 functions deep from the usort, making it very difficult to track down.  

After finally figuring out the exception was somehow related, we searched google and found this bug report.  I'm sure we can agree that the minor act of catching an exception should not result in usort throwing a warning message.  This bug is a huge timewaster :(
 [2012-02-21 22:56 UTC] eric_haney at yahoo dot com
It took me a while to figure out that some code called from usort was throwing, catching, and (gracefully) handling an Exception.  Then I found this post.  Quite frustrating.

I turned off warnings with ini_set before calling usort, then turned them on again after.  This is an effective workaround for now, but I'd love to clean that nastiness out of my code.

It is also my opinion that usort should be allowed to change the elements in the array.  EG: an instance variable of an object may be lazy-loaded as a result of a method call from within a usort callback.  Should a warning really be issued in that case?
 [2012-02-24 18:04 UTC] keith at breadvault dot com
This same problem arises when using Mockery to mock the object whose method is 
being used by usort(), even though the method itself neither is mocked nor handles 
any exceptions. The proxy generated by Mockery must wrap the target class's 
methods with some exception-handling code.

Unfortunately this forced me to code a workaround that would not use usort. My 
hack extracts from the objects in the array the values being sorted on, sorts that 
array of values using asort() (to preserve the keys), and finally rebuilds the 
list of objects using the keys in the order that they appear in the asorted list 
of values. Yuck.
 [2012-08-08 17:53 UTC] mbrowne83 at gmail dot com
This will probably be obvious to most, but I just wanted to mention that you can always prefix the usort function with the @ symbol to prevent the warning...of course that would also suppress any other types of notices or warnings that might occcur anywhere within the sorting function...
 [2013-06-17 11:11 UTC] andrejs dot verza at gmail dot com
Php 5.4.16 also fails with this.
Still the same status for 3 and a half years old bug?!
 [2013-07-09 07:21 UTC] jakub dot lopuszanski at nasza-klasa dot pl
I'd like to add, that you do not have to throw an exception to get this warning.
Mere creating it, also triggers the warning, as in:

<?php
function comp($a,$b){
  @new Exception("dupa");
}
$a =array(1,2,3);
  usort($a, 'comp');
var_dump($a);
?>

PHP Warning:  usort(): Array was modified by the user comparison function in 
/home/jlopuszanski/test.php on line 6
 [2013-08-19 18:23 UTC] m at rtin dot so
I ran into a similar issue, i'm sure it'll require the same patch as it's the 
backtrace causing the problem but worth noting it doesn't require an exception to 
trigger this, just a backtrace.

$ cat usort.php
<?php
 
set_error_handler(function($errno, $errstr) {
  $bt = debug_backtrace();
  var_dump($errstr);
});
 
$arr = [1, 2];
usort($arr, function($a, $b) use ($arr) {
  trigger_error('test');
  return $a > $b;
});
 
$ php usort.php
string(4) "test"
string(59) "usort(): Array was modified by the user comparison function"
 [2013-08-19 19:04 UTC] m at rtin dot so
Sorry the use () isn't relevant, I forgot to remove it when simplifying my test 
case
 [2015-07-18 14:17 UTC] nikic@php.net
This has been fixed in PHP 7.
 [2016-05-27 21:01 UTC] albert at mediatribe dot net
A notice within the compare function will also trigger this notice. For example:

function test($a, $b) {
  return strnatcmp($a['name'], $b['name']);
}
$e = array(
  array(
    'not-name' => 'a',
  ),
  array(
    'name' => '',
  ),
);
uasort($e, 'test');

(this is tested on PHP 5.3)
 [2017-03-28 00:07 UTC] love at sickpeople dot se
This bug should be closed as Fixed, since it's fixed in PHP 7 and 5.6 does not have active support.
 [2017-12-06 18:14 UTC] nikic@php.net
-Status: Assigned +Status: Closed
 [2017-12-06 18:14 UTC] nikic@php.net
Closing as this is fixed in PHP 7 and PHP 5.6 is out of active support.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Apr 18 21:01:29 2024 UTC