Search code examples
phpclassrecursionforeachhierarchical

Recursive function inside class with foreach changes public value where it shouldn't


Ok, I'm really stucked with this. I hope you can help me.

I have my class, used to manage hierarchical data. The input is a plain array with the following structure (just an example):

$list = array(
(object) array('id' => 1, 'nombre' => 'Cámaras de fotos', 'parentId' => null),
(object) array('id' => 2, 'nombre' => 'Lentes', 'parentId' => null),
(object) array('id' => 3, 'nombre' => 'Zoom', 'parentId' => 2),
(object) array('id' => 4, 'nombre' => 'SLR', 'parentId' => 1),
(object) array('id' => 5, 'nombre' => 'Primarios', 'parentId' => 2),
(object) array('id' => 6, 'nombre' => 'Sensor APS-C', 'parentId' => 4),
(object) array('id' => 7, 'nombre' => 'Full-frame', 'parentId' => 4),
(object) array('id' => 8, 'nombre' => 'Flashes', 'parentId' => null),
(object) array('id' => 9, 'nombre' => 'Compactas', 'parentId' => 1)
);

I input the data to the class this way:

$Hierarchical = new Hierarchical;
$Hierarchical->plain = $list;

Then I have a public function (createTree) to create a multidimensional array representation of the list. It works perfectly. It can return the result or store it inside $this->tree.

As you can see, this is very simple. It calls private function iterateTree, which is the recursive function.

class Hierarchical {

    public $plain = array();
    public $tree = array();

    public function createTree($parentId=0, $return=false) {

    $tree = $this->iterateTree($parentId);

    if(!$return) {
        $this->tree = $tree;
    } else {
        return $tree;
    }

    }


    private function iterateTree($parentId) {

    $resArray = array();

    foreach($this->plain as $item) {

        if($item->parentId == $parentId) {

            $children = $this->iterateTree($item->id);

            if( count($children) > 0 ) {
                $item->children = $children;
            }

            $resArray[] = $item;

        }

    }

    return $resArray;

    } 

}

So far so good. It works fine.

BUT... The problem appears when I want to use $this->plain after calling createTree(). Instead of returning the original dataset, it returns some kind of mix between the original input, with all their children appended (similar to $this->tree).

I can't figure out why the content of $this->plain is being changed, neither in the both functions used I'm changing it's content.

I've tried unseting the variables inside the foreach, after the foreach, even passing the original array as an argument and not using $this->plain at all inside the recursive function. Nothing worked.

I'm also not using any other function inside the class that could change it's value.

It's a total mistery!


Solution

  • In your foreach loop $item will be a reference to the object in the array, so you are changing that same object in the line

    $item->children = $children;
    

    This will affect the object referred to in the original arrays $list and $this->plain.

    One solution may be to clone $item within your foreach loop.