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

北战南征 提交于 2019-12-14 04:20:52

问题


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!


回答1:


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.




回答2:


According to Doug's answer, the correct function is: (added $itemAux = clone $item)

private function iterateTree($parentId) {

    $resArray = array();

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

        $itemAux = clone $item;

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

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

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

            $resArray[] = $itemAux;

        }

    }

    return $resArray;

} 



回答3:


To add to Doug's answer, although the manual says that "objects are not passed by reference" (http://www.php.net/manual/en/language.oop5.references.php), it may instead help to think of objects as a completely separate entity from any variables that may "contain" them, and that they are actually passed everywhere by reference...

class testClass
{
    public $var1 = 1;
}

function testFunc($obj)
{
    $obj->var1 = 2;
}


$t = new testClass;
testFunc($t);
echo $t->var1; // 2

So when you do $item->children = $children;, you are in fact affecting each original object in that $plain array.



来源:https://stackoverflow.com/questions/10729211/recursive-function-inside-class-with-foreach-changes-public-value-where-it-shoul

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!