4

edit:do not read the related topic, the answer below is clear and gives the solution, while the other topic just states the issue.

I have something weird here

My code looks like this:

        var_dump($resultFlatTree);
        foreach($resultFlatTree as &$element)
        {
            /*if(isset($element["action"]) && $element["action"] == "new")
            {
                //let's save the original ID so we can find the children
                $originalID = $element["id"];
                //now we get the object
                $newObject = $setUpForDimension->createAnObject($dimension,$element,$customer);
                $element['id'] = $newObject->getId();
                echo "new";
                //and let's not forget to change the parent_id of its children
                $arrayFunctions->arrayChangingValues($resultFlatTree,"parent_id",$element['id'],$originalID);
                $em->persist($newObject);                                                             
            } */               
        }            
        $em->flush();
        var_dump($resultFlatTree);

the code inside the foreach is commented to be sure that it's not what I'm doing that's changing the array.

here the array before the foreach:

array(3) {
  [0]=>
  array(10) {
    ["id"]=>
    int(2)
    ["name"]=>
    string(7) "Revenue"
    ["code"]=>
    string(6) "700000"
    ["sense"]=>
    string(2) "CR"
    ["lft"]=>
    int(1)
    ["lvl"]=>
    int(2)
    ["rgt"]=>
    int(1)
    ["root"]=>
    int(1)
    ["$$hashKey"]=>
    string(3) "00D"
    ["parent_id"]=>
    int(1)
  }
  [1]=>
  array(10) {
    ["id"]=>
    int(3)
    ["name"]=>
    string(7) "Charges"
    ["code"]=>
    string(6) "600000"
    ["sense"]=>
    string(2) "DR"
    ["lft"]=>
    int(3)
    ["lvl"]=>
    int(2)
    ["rgt"]=>
    int(4)
    ["root"]=>
    int(1)
    ["$$hashKey"]=>
    string(3) "00P"
    ["parent_id"]=>
    int(4)
  }
  [2]=>
  array(10) {
    ["id"]=>
    int(4)
    ["name"]=>
    string(6) "Energy"
    ["code"]=>
    string(6) "606000"
    ["sense"]=>
    string(2) "DR"
    ["lft"]=>
    int(2)
    ["lvl"]=>
    int(1)
    ["rgt"]=>
    int(5)
    ["root"]=>
    int(1)
    ["$$hashKey"]=>
    string(3) "00E"
    ["parent_id"]=>
    int(1)
  }
}

and then after:

array(3) {
  [0]=>
  array(10) {
    ["id"]=>
    int(2)
    ["name"]=>
    string(7) "Revenue"
    ["code"]=>
    string(6) "700000"
    ["sense"]=>
    string(2) "CR"
    ["lft"]=>
    int(1)
    ["lvl"]=>
    int(2)
    ["rgt"]=>
    int(1)
    ["root"]=>
    int(1)
    ["$$hashKey"]=>
    string(3) "00D"
    ["parent_id"]=>
    int(1)
  }
  [1]=>
  array(10) {
    ["id"]=>
    int(3)
    ["name"]=>
    string(7) "Charges"
    ["code"]=>
    string(6) "600000"
    ["sense"]=>
    string(2) "DR"
    ["lft"]=>
    int(3)
    ["lvl"]=>
    int(2)
    ["rgt"]=>
    int(4)
    ["root"]=>
    int(1)
    ["$$hashKey"]=>
    string(3) "00P"
    ["parent_id"]=>
    int(4)
  }
  [2]=>
  &array(10) {
    ["id"]=>
    int(4)
    ["name"]=>
    string(6) "Energy"
    ["code"]=>
    string(6) "606000"
    ["sense"]=>
    string(2) "DR"
    ["lft"]=>
    int(2)
    ["lvl"]=>
    int(1)
    ["rgt"]=>
    int(5)
    ["root"]=>
    int(1)
    ["$$hashKey"]=>
    string(3) "00E"
    ["parent_id"]=>
    int(1)
  }
}

As you can see, the last element is now changed and is by reference. This completely messes up the processes I do with the array afterward.

Is that normal behavior ? How can I avoid it ?

Eagle1
  • 810
  • 2
  • 12
  • 30
  • possible duplicate of [PHP Pass by reference in foreach](http://stackoverflow.com/questions/3307409/php-pass-by-reference-in-foreach) – Rhumborl Dec 15 '14 at 09:49
  • @Rhumborl This doesn't seem to be a duplicate of that. His question isn't about the iteration variable, it's about the array itself. – Barmar Dec 15 '14 at 09:52
  • actually it's the same problem but in this thread the solution isn't provided. the answer below did by quoting the manual so I guess better to leave my question. – Eagle1 Dec 15 '14 at 10:04

1 Answers1

3

When you pass by reference to a foreach statement, you really should read the docs :)

http://php.net/manual/en/control-structures.foreach.php

In order to be able to directly modify array elements within the loop precede $value with &. In that case the value will be assigned by reference.

<?php
$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
    $value = $value * 2;
}
// $arr is now array(2, 4, 6, 8)
unset($value); // break the reference with the last element
?>

Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset().

Basically, it's saying that when you pass by ref, it will remain locked on the last item due to an internal pointer.

The second user-comment at 40 points:

"Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset()."

I cannot stress this point of the documentation enough! Here is a simple example of exactly why this must be done:

<?php
$arr1 = array("a" => 1, "b" => 2, "c" => 3);
$arr2 = array("x" => 4, "y" => 5, "z" => 6);

foreach ($arr1 as $key => &$val) {}
foreach ($arr2 as $key => $val) {}

var_dump($arr1);
var_dump($arr2);
?>

The output is:

array(3) { ["a"]=> int(1) ["b"]=> int(2) ["c"]=> &int(6) }
array(3) { ["x"]=> int(4) ["y"]=> int(5) ["z"]=> int(6) }

Notice how the last index in $arr1 is now the value from the last index in $arr2!

There are more comments which you will find interesting if you look for "reference" in that link.

tl;dr: It's a bit funny/buggy/weird/un-patched. Understand what the implications are as you write your code and make space for them.

Fluffeh
  • 33,228
  • 16
  • 67
  • 80
  • Glad I could assist. The best thing is that I learned something today as well. That's why I love SO. You can learn by answering! – Fluffeh Dec 15 '14 at 10:09