Рекурсивная функция внутри класса с foreach изменяет общедоступную ценность, где она не должна

Хорошо, я действительно застрял в этом. Я надеюсь, что вы можете мне помочь.

У меня есть мой класс, используемый для управления иерархическими данными. Вход представляет собой простой массив со следующей структурой (просто пример):

$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) ); 

Я ввожу данные в класс следующим образом:

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

Затем я createTree публичную функцию ( createTree ) для создания многомерного представления массива списка. Он работает отлично. Он может вернуть результат или сохранить его в $this->tree .

Как вы можете видеть, это очень просто. Он вызывает частную функцию iterateTree , которая является рекурсивной функцией.

 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; } } 

Все идет нормально. Он работает нормально.

НО … Проблема возникает, когда я хочу использовать $this->plain после вызова createTree() . Вместо того, чтобы возвращать исходный набор данных, он возвращает какой-то микс между исходным входом, причем все их дочерние элементы добавлены (аналогично $this->tree ).

Я не могу понять, почему изменяется содержимое $this->plain , ни в обеих используемых функциях я не изменяю его содержимое.

Я попытался пропустить переменные внутри foreach , после foreach , даже передав исходный массив в качестве аргумента и не используя $this->plain вообще внутри рекурсивной функции. Ничего не получилось.

Я также не использую какую-либо другую функцию внутри класса, которая могла бы изменить ее значение.

Это полная мистерия!

Solutions Collecting From Web of "Рекурсивная функция внутри класса с foreach изменяет общедоступную ценность, где она не должна"

В вашем цикле foreach $item будет ссылкой на объект в массиве, поэтому вы меняете тот же объект в строке

 $item->children = $children; 

Это повлияет на объект, указанный в исходном массиве $list и $this->plain .

Одним из решений может быть клонирование $item в вашем цикле foreach.

Согласно ответу Дуга, правильная функция: (добавлено $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; } 

Чтобы добавить к ответу Дуга, хотя в руководстве говорится, что «объекты не передаются по ссылке» (http://www.php.net/manual/en/language.oop5.references.php), он может вместо этого помочь подумать объектов как полностью отдельный объект из любых переменных, которые могут «содержать» их, и что они фактически передаются везде по ссылке …

 class testClass { public $var1 = 1; } function testFunc($obj) { $obj->var1 = 2; } $t = new testClass; testFunc($t); echo $t->var1; // 2 

Поэтому, когда вы делаете $item->children = $children; , вы фактически затрагиваете каждый оригинальный объект в этом $plain массиве.