问题
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