在PHP中克隆父子树,从子节点开始,避免无休止的递归

我在
PHP中有一个面向对象的父子树,我想要克隆.

困难的部分是对树的访问并不总是通过根,但有时通过根的子进程,如下所示:

[Root]
  -- [Element1] START CLONE
       -- [Element3]
       -- [Element4]
  -- [Element2]
       -- [Element5]

所以我想做的是克隆整个树,通过调用$new = clone $element1;

__clone()方法声明还必须克隆每个子节点,并且,如果出现图示的情况*,则还必须克隆父节点.

* Root在Element1中显式设置为父级,因此系统可以识别这种情况并对其执行某些操作.

问题是,从Element1开始克隆操作时,还必须克隆Root. Root的克隆过程规定必须克隆所有子元素,因此再次调用Element1的克隆操作,然后重复相同的克隆过程,产生无限循环.

此外,Root不会包含Element1的第一个克隆,但它会生成自己的克隆以作为子元素添加.然后,Element1将Root作为其父级,但Root将不具有与子级相同的Element1.

我希望我能以清晰的方式提出问题,有人可以帮我找到解决方案.

编辑:

最终解决方案

/**
 * The $replace and $with arguments allow a custom cloning procedure. Instead of
 * being cloned, the original child $replace will be replaced by $with.
 */
public function duplicate($replace = null, $with = null) {
    // Basic cloning
    $clone = clone $this;
    // If parent is set
    if(isset($this->parent)) {
        // Clone parent, replace this element by its clone
        $parentClone = $this->parent->duplicate($this, $clone);
        $clone->parent = $parentClone;
    }

    // Remove all children in the clone
    $clone->clear();

    // Add cloned children from original to clone
    foreach($this->getChildren() as $child) {
        if($child === $replace)
            // If cloning was initiated from this child, replace with given clone
            $childClone = $with;
        else
            // Else duplicate child normally
            $childClone = $child->duplicate();

        // Add cloned child to this clone
        $clone->add($childClone);
    }

    return $clone;
}

最佳答案 首先,简化您的示例:

[Root]
  -- [Element1] START CLONE
       -- [Element3]

然后你做的不同,我认为你有三个操作

>公共克隆方法.
>自我克隆操作,返回带子节点的克隆,但不返回父节点.
>单克隆操作,返回没有子节点的克隆.

类之外的代码使用公共克隆方法.但是__clone()方法不能使用该方法,否则会遇到您所描述的循环循环问题.所以__clone()实现必须使用其他方法.

将cloneSelf和cloneSingle方法添加到您的类中,使它们受到保护,因此继承的类可以调用它们,但它们不公开.

然后在__clone()实现中使用它们:

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

这些方法还可以帮助您在没有父级的情况下进行克隆.

点赞