0

I'm currently touching some legacy code in an attempt to clean it and have come across something of a puzzle. It's not even remotely mission critical that I resolve it, so consider this a sporting question at most, but it's still perplexing me. Colour me curious.

This code:

// dummy data for the sake of testing / abstract visualisation;
// x is thrown in as an experiment:
$workTables = array(
    array('x' => 5, 'Name' => 'foo3', 'Update_time' => '2013-04-04 04:40',),
    array('x' => 4, 'Name' => 'foo4', 'Update_time' => '2013-04-01 04:40',),
    array('x' => 3, 'Name' => 'foo2', 'Update_time' => '2013-04-04 09:40',),
    array('x' => 2, 'Name' => 'foo1', 'Update_time' => '2013-04-12 04:40',),
    array('x' => 1, 'Name' => 'foo5', 'Update_time' => '2012-12-04 04:40',),
);

// original legacy code:
if (!empty($workTables)) {
    $sort = array();
    foreach ($workTables as $key => $value) {
        $sort[$key]["Update_time"] = $value["Update_time"];
    }
    array_multisort($workTables, SORT_ASC, SORT_STRING, $sort);
}

...while throwing twelve notices ("Notice: Array to string conversion ..."), works as expected, by ordering all elements in $workTables according to Update_time.

(The 'x' column is part of my attempt to rule out that it has anything to do with Name, which in the original code is a fixed prefix followed by a timestamp (e.g. work_table_1367940392, work_table_1367940395, ...), making sorting by Name equivalent to sorting by Update_time.)

Obviously, since I don't want to program by coincidence, at the very least this is going to be replaced with:

// new code:
if (!empty($workTables)) {
    $sort = array();
    foreach ($workTables as $key => $value) {
        $sort[$key] = $value["Update_time"];
    }
    array_multisort($sort, SORT_ASC, SORT_STRING, $workTables);
}

...which conforms to the description of array_multisort(), also does what we want, and doesn't throw Notices in our face.

But what I'm really interested in is why the old code works (notices notwithstanding).

A partial reason seems to be the behaviour of asort() and co., which have the (partly) undocumented feature that they can work with multi-dimensional arrays by acting according to the contents of the array, working through the structure from "left to right" (at least in PHP 5.4.7 (cli))... but... I'm stuck trying to comprehend what's making $workTables and $sort 'interchangable'.

I've tried to look at PHP's C source code to figure it out, but I'm stuck at trying to understand what happens here:

/* Do the actual sort magic - bada-bim, bada-boom. */
zend_qsort(indirect, array_size, sizeof(Bucket **), php_multisort_compare TSRMLS_CC);

...since my knowledge of C has rusted terribly and zend_qsort() itself is just flatly out of my league.

Any takers?

Keep in mind I'm not desperate for an answer, so don't sink too much time into this, but maybe someone else likes a puzzle, too? :)

Personally, I've invested some time into this purely because I prefer understanding code thoroughly, especially when I'm trying to clean it up - even if it's code that works only by coincidence. I've just reached a dead-end when it comes to further comprehension, so stackoverflow seemed like the best chance for further enlightenment.

So, if you have an idea about what's going on behind the scenes (I suspect it's something trivial I've overlooked; that tends to be my problem after going around in circles for a while), I'd love to hear it! :)

hakre
  • 193,403
  • 52
  • 435
  • 836
pinkgothic
  • 6,081
  • 3
  • 47
  • 72
  • the bada-bim, bada-boom comment really gives it away.. – raidenace May 07 '13 at 16:20
  • @Raidenace: Haha, I know, right? It explains *everything*. :) (To be fair, I thought it was great - humourous comments are a rare (if ambiguous) pleasure to behold.) – pinkgothic May 08 '13 at 07:15

1 Answers1

1

From what I understand, an array is being created and stored in $sort[$key]. Then, once you call array_multisort(), it happens to convert the array back into a string and sort using that string as the value that defines the order -- just as the warning says. Then, this string is used as the order-defining value.

How is this conversion array->string performed? Just doing echo $anArrayValueHere; is not helpful; it prints out Array. Similar trick, echo "" . $anArrayValueHere . ""; also doesn't yield anything useful. (But neither does it yield a notice!)

It's quite possible that serialize() is used. Presuming that, let's see what would get used as a order-defining value:

#!/usr/bin/php5
<?php
$sort[0]["Update_time"] = '2012-12-04 04:40';
echo serialize($sort[0]);

then

$ ./php tmp.php
a:1:{s:11:"Update_time";s:16:"2012-12-04 04:40";}

It looks like the key would be sufficiently definitive to yield something usable to define sort order just as well as just the Update_time value would.

I'm not sure that serialize() is used, but if it is, I think this is the explanation.

Community
  • 1
  • 1
Ivan Vučica
  • 9,529
  • 9
  • 60
  • 111
  • Hmm, I didn't see any evidence of `serialize()` (and it would be strange for `serialize()` to throw the conversion warning), but... that could also just be due to my complete disorientation in the C code. :) I'm fairly sure the ordering itself is due to `sort()` and co's multidimensional-array handling (`sort()` also uses `zend_qsort()`), the odder question to me is still the interchangability of the arrays. – pinkgothic May 08 '13 at 07:22
  • "(But neither does it yield a notice!)" doesn't hold true on my machine, by the way, but that just as a sidenote for completion's sake. I gotta say, though, thank you so much for your stab at this! :) I wish I had more than one upvote to give you. – pinkgothic May 08 '13 at 07:25