Clone parent-child tree in PHP, start from child and avoid endless recursion?

First of all, simplify your example: Root -- Element1 START CLONE -- Element3 Then differ between what you do, I think you have got three operations public clone method A self-clone operation that returns a clone with children, but not the parent A single-clone operation that returns a clone w/o children Code outside of your classes is using the public clone method. But the clone() method must not use that method, otherwise you run into the circular looping problem you've described. So the clone() implementation must use other methods Add a cloneSelf and cloneSingle method to your class, make them protected, so inherited classes can call them but they are not publicly available Then make use of them in the clone() implementation: public function clone() { // clone the parent $parent = $this->getParent(); $parentClone = $parent->cloneSingle(); // clone all children of parent which includes $this $selfClone = NULL; foreach($parent->getChildren() as $child) { $childClone = $child->cloneSelf(); if (!$selfClone && $child === $this) $selfClone = $childClone; $parentClone->addChild($childClone); } assert('$selfClone'); return $selfClone; } public function __clone() { $message = 'Clone operator is not allowed, use clone() method instead.'; throw new BadMethodCallException($message); } These methods will also help you to clone in case there is no parent.

First of all, simplify your example: Root -- Element1 START CLONE -- Element3 Then differ between what you do, I think you have got three operations public clone method. A self-clone operation that returns a clone with children, but not the parent. A single-clone operation that returns a clone w/o children.

Code outside of your classes is using the public clone method. But the __clone() method must not use that method, otherwise you run into the circular looping problem you've described. So the __clone() implementation must use other methods.

Add a cloneSelf and cloneSingle method to your class, make them protected, so inherited classes can call them but they are not publicly available. Then make use of them in the __clone() implementation: public function clone() { // clone the parent $parent = $this->getParent(); $parentClone = $parent->cloneSingle(); // clone all children of parent which includes $this $selfClone = NULL; foreach($parent->getChildren() as $child) { $childClone = $child->cloneSelf(); if (!$selfClone && $child === $this) $selfClone = $childClone; $parentClone->addChild($childClone); } assert('$selfClone'); return $selfClone; } public function __clone() { $message = 'Clone operator is not allowed, use clone() method instead. '; throw new BadMethodCallException($message); } These methods will also help you to clone in case there is no parent.

Thanks for the suggestion. I think the reasoning is good, but according to the PHP Manual the __clone() method is called from the object clone, after the standard shallow cloning has been completed. The object you get from the clone $object operation will not be $selfClone from this method.

Also because of this, we cannot compare each child of the parent with the current object using '===', since the clone is nowhere among the children of the parent of the original. – RemiX Oct 21 at 10:07 @RemiX: Yeah right I mixed it. However you can inversion the logic by providing your own public clone method instead, I edited the answer.

– hakre Oct 21 at 11:19 Ok, apart from the fact that you cannot call a method clone(), your answer was now very useful. I created a custom duplicate() method, which first calls the clone operator. Then it clones its parent and replaces itself with its clone.

This allows for more recursion when the parent has yet another parent, AND I won't have to reinvent cloning (I can still use the clone operator). I edited the final code into my question. Thanks for your help.

Unless someone comes up with something that makes this solution look like crap, you'll get the bounty. – RemiX Oct 23 at 12:18.

Let's call it $called_from Based on the value of that parameter you know what to do: when the param has the default value 'external', or has the value child then clone was called from an external place, so you will call __clone() on parent with, sending 'child' as value when __clone() is finally called on root node with 'child' or 'external' value set to $called_from, it starts the real cloning process by calling __clone() with $called_from set to 'parent' Edit I was not aware of the builtin clone keyword. So, you could create a base class that all your tree objects are inheriting from - this class could have a static, variable that will indicate what clone has to act like when set to true, the real clone algorithm will be executed, otherwise, will __clone() the parent object the default value is false, and only the root node sets this to true, just before it starts cloning the children This base class can also override the __clone() method, to implement this algorithm in a single place.

Sounds like a decent solution, except I don't know how to pass a parameter with the 'clone' operation. Is that even possible? – RemiX Oct 8 at 14:33 Ok, then one more hurdle: if I clone the parent like this: $this->parent = clone $this->parent (in the __clone() method), its parent will start cloning all its (still original) children (of which $this is one) but it will be another instance than $this itself.

Going from child to parent and from parent back to child will get you to another clone of the same child.. any suggestions on that? Or is this unclear? – RemiX Oct 9 at 9:40.

I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.

Related Questions