1

I have a user defined sort function which uses a 'global' declaration in order to refer to a multi-dimensional array when deciding on the correct sort order. It used to work fine under PHP 5.1.6 but now fails under 5.3.3.

The code throws PHP warning:

PHP Warning: uksort(): Array was modified by the user comparison function

But the code definitely does not modify the array.

This code duplicates the problem:

$arr = array();

$arr['i1']['val1'] = 99;
$arr['i1']['val2'] = 100;

$arr['i2']['val1'] = 89;
$arr['i2']['val2'] = 101;


function cmp($a, $b)
{
    global $arr;

    if ($arr[$a]['val2'] > $arr[$b]['val2']) { return 1; }
    if ($arr[$a]['val2'] < $arr[$b]['val2']) { return -1; }
    return 0;
}

if (uksort($arr, 'cmp'))
{
    echo "success";
}
else
{
    echo "failure";
}
hakre
  • 193,403
  • 52
  • 435
  • 836
Claud
  • 937
  • 1
  • 12
  • 27
  • you are modifying(sorting) `$arr` user inside the sort `global $arr;` – bansi Aug 29 '13 at 09:51
  • My assumption is that as uksort takes a reference to the variable in the global table (and changes it inside it's own function, so it's changed compared to the global to be updated when the function returns) and the cmp function creates another reference to the global symbol table, this again does modify the array then. But I'm not so well with the PHP internals. I assume this has to do with the fact that variables passed by references are "written back" later at the time the function ends/returns. – hakre Aug 29 '13 at 09:55
  • The warning is there to protect you and tell you about you were doing some dangerous variable handling in the past. Some PHP behavior is unexpected and undefined, in PHP this is often related when manipulating an array while it is operated upon. Manipulating can already mean to access it. – hakre Aug 29 '13 at 10:17

2 Answers2

1

If you're not going to sort by the actual keys, don't use uksort but usort or uasort:

function cmp($a, $b) {
    return $a['val2'] - $b['val2'];
}

uasort($arr, 'cmp');
hakre
  • 193,403
  • 52
  • 435
  • 836
deceze
  • 510,633
  • 85
  • 743
  • 889
  • `uasort` is better as it maintains associative keys, which seems to be what OP is trying to do. – Niet the Dark Absol Aug 29 '13 at 09:48
  • Well, if I read the question right, OP thinks the array wasn't modified, PHP told it was modified and now OP wonders where the modification happens. Suggesting an alternative way to sort the array might be nice to have, but I don't see this is answering the question. – hakre Aug 29 '13 at 09:52
  • 1
    @hakre True, but sometimes sidestepping the whole issue if you're doing something stupid anyway is the best answer... I'd love to know why PHP is complaining as well, but I don't have the inclination to investigate that in detail ATM. ;) – deceze Aug 29 '13 at 09:54
  • Our application is not quite the same as the example given, which was used to make the point as succinctly as possible. Agreed, in the example case posted, 'uasort' is the ideal solution. – Claud Aug 29 '13 at 20:42
0

As sidestepping the issue is so popular, here is the cause of your issue:

global $arr;

I'm sure you sensed that aready, to solve that, remove the line and replace $arr with $GLOBALS['arr']. This removes the modification message and you'er accessing the global variable from the symbol table and not the one uksort is currently operating on.

Example:

<?php
$arr = array();

$arr['i1']['val1'] = 99;
$arr['i1']['val2'] = 100;

$arr['i2']['val1'] = 89;
$arr['i2']['val2'] = 101;


function cmp($a, $b)
{
    if ($GLOBALS['arr'][$a]['val2'] > $GLOBALS['arr'][$b]['val2']) { return 1; }
    if ($GLOBALS['arr'][$q]['val2'] < $GLOBALS['arr'][$b]['val2']) { return -1; }
    return 0;
}

if (uksort($arr, 'cmp'))
{
    echo "success\n";
}
else
{
    echo "failure\n";
}

print_r($arr);

Output for 5.3.23 - 5.5.3: (other versions crash/misbehave)

success
Array
(
    [i1] => Array
        (
            [val1] => 99
            [val2] => 100
        )

    [i2] => Array
        (
            [val1] => 89
            [val2] => 101
        )

)

Ups, maybe that's why you see a warning in more recent versions: (Demo: http://3v4l.org/DkK3v)

enter image description here

hakre
  • 193,403
  • 52
  • 435
  • 836