views:

529

answers:

6

I have a dataset obtained from MySQL that goes like this:

Array
(
    [0] => Array
        (
            [views] => 14
            [timestamp] => 06/04
            [views_scaled] => 4.9295774647887
            [unix_time] => 1239022177
        )

    [1] => Array
        (
            [views] => 1
            [timestamp] => 19/04
            [views_scaled] => 0.35211267605634
            [unix_time] => 1240194544
        )

        ...
        ...
        ...

) 1

(it's post-processed, 'timestamp' was really a timestamp before, but that doesn't matter anyways)

The array is stored on $results, and in the middle of my code I do something like this:

$results = array_merge($results, $new_days);
$a = $results;
foreach ($results as $row)
{
    $unix_time[] = $row['unix_time'];
}
$b = $results;

The problem: $a and $b are both different. The first one shows the array as it's supposed to, and the second one has the same count(), but it's fourth element is a duplicate from the last one. As far as I know, I'm not passing anything by reference, so $results Isn't meant to change (maybe the pointer, but not it's content). I'm using PHP 5.2.4 on Mac OS X 10.5.2.

The obvious question: Is this somehow the intended behavior, a bug or I'm doing something wrong here? (not a boolean answer please ;)


EDIT: Thank you all for the interest, I don't know exactly how much extra code should I post, I don't do much before except for retrieving the data from the DB and a foreach to parse the timestamp and build a new array ($new_days) for the missing days. This is all working fine.

This code goes after the one I've posted early:

array_multisort($unix_time, SORT_ASC, $results);
$days = implode('|', array_pluck('timestamp', $results));
$views = implode('|',  array_pluck('views', $results));
$views_scaled = implode(',', array_pluck('views_scaled', $results));

(array_pluck() is a custom function to generate an array from a column in a typical DB-dumped dataset)


EDIT 2: Thanks again, here's the full snippet and the output from the $results array $a and $b (also referenced in the code's comments).

A: 

I can't imagine how that is intended behavior. There must be something else going on here. Can you isolate the behavior to a piece of code that's small enough to post here? Probably if you do the bug will become obvious.

GoatRider
A: 

I would also say there's something else going on.

I wrote this:

<?php

$a = array('bob','sam','tom','harry');
$b = array();
$c = array();

foreach($a as $item) {
        $c[] = $item;
}
$b = $a;

print_r($a);
print_r($b);

And got:

php ./test.php
Array
(
    [0] => bob
    [1] => sam
    [2] => tom
    [3] => harry
)
Array
(
    [0] => bob
    [1] => sam
    [2] => tom
    [3] => harry
)

I am running PHP 5.2.8, though.

jeremib
Thanks for the testing!
fandelost
A: 

I think your problem is that the result set isn't really an array, it's a mysql resultset object that acts like an array.

I think if you go through each row, assigning it to a fresh array, then do the merge, it'll act properly.

Ian
I don't think so, since it's retrieved from CodeIgniter's function result_array(), and it looks ok after the merge, the problem arises after the foreach loop.
fandelost
+1  A: 

Inspecting your code snippet, really quickly (just about to leave the office for the day), it is probably to do with something passing by reference in your (first) loop. Try using normal by value and just storing everything into a fresh result array. (will remove any mysteries that could be going on). Could also try making the second $row in the second foreach a different name.. beats me - can't tell you with really looking at this more.

also this line and following block of code won't execute

if ($last_day != $day_before_this_one AND $last_day)

could have something to do with it, new days will never fill up and the merge could be doing something funky.

Wouldn't call this an answer but its a start to look at I guess

jim
Thank you Lou, I'll try your suggestions and let everyone know. But in the meantime, that line you highlighted *does* execute, not the first time though (this is obviously intended to avoid an infinite loop). It's the one responsible for filling the $new_days array that will end up been part of $results.
fandelost
I changed the name of the value variable in the second foreach loop, from $row to $row2 and it worked perfectly! Thank you very much, and thanks everyone for your contributions. I still think this is really a workaround for some bug like Evert suggested, but if someone understands better the inner workings of this situation, I'd be glad to read it. I'm just not adventurous enough to go to bugs.php.net and fill out the TPS reports.
fandelost
It's already been filed, and the status is "Bogus" - http://bugs.php.net/bug.php?id=29992
Calvin
fandelost, and anyone else interested:http://derickrethans.nl/files/phparch-php-variables-article.pdfpdf article well written but should explain a bit more about php variables. Written for php4 but i don't think anything has changed in php5 in relation to this except for object ref handling
jim
A: 

Unless I'm mistaken, this was a PHP bug a while back. I don't know the details, but arrays and references have been screwed up for a little bit.

Evert
It's not a bug: http://bugs.php.net/bug.php?id=29992
Calvin
Thanks for the reference Calvin!
fandelost
Yea, np. On the surface of it, it's kind of a counter-intuitive behavior, but the 3rd to last bug report comment gives a pretty good break down of what's going on.
Calvin
A: 

The problem is the first foreach loop, as was already mentioned.

Here's the reasoning...

<?
// set up an example array and loop through it using references (&)
$my_array = array(1,2,3,4);
foreach($my_array as &$item)
{
  $item = $item+.1;
}
// 1st loop, $item points to: $my_array[0], which is now 1.1
// 2nd, $item -> $my_array[1], which is now 2.1
// 3rd, $item -> $my_array[2], which is now 3.1
// 4th, $item -> $my_array[3], which is now 4.1
// looping done, but $item is still pointing to $my_array[3]

// next foreach loop
foreach($my_array as $item)
{
  var_dump($my_array);
  print $item."<br>";
}
// notice the & in the output of the var_dump, if you actually run this code.
// 1st loop: the value of $my_array[0] is assigned to $item, which is still a pointer/reference to $my_array[3]
// first loop.. array(1.1,2.1,3.1,1.1) // grabbing [0] and putting into [3] 
// next loop... array(1.1,2.1,3.1,2.1) // grabbing [1] and putting into [3]
// next loop... array(1.1,2.1,3.1,3.1) // grabbing [2] and putting into [3] (here's where the magic happens!)
// last loop... array(1.1,2.1,3.1,3.1) // grabbing [3] and putting into [3] (but [3] is the same as [2] !!!)
?>

I hope this makes sense! Basically the second to last value will be repeated because the last value is replaced during the second loop.

Thank you Josh, it makes sense indeed and I know understand what's going on there. Definitely misleading and not properly documented behavior though. Rule of thumb: use different names for variables that were passed by reference (or unset before using them a second time).
fandelost
can i get the correct answer? :)