PHP 克隆

###什么是克隆

克隆就是完全复制另一个对象,二者独立,互不影响。你可能说直接用变量赋值不就完了吗?但是对于对象来说,简单的赋值并不能实现克隆了,因为对象是通过伪引用传递的,比如:

<?php
class Sample {
    private $a = 1;

    public function plus() {
        $this->a ++;
    }

    public function foo() {
        echo "a={$this->a}";
    }
}

$obj = new Sample();
$obj->foo(); // 输出a=1

$obj2 = $obj;
$obj2->plus(); // +1
$obj2->foo(); // 输出a=2
$obj->foo(); // 输出a=2,说明$obj也发生了变化,显然这不是我们想看到的
?>

###克隆

PHP提供了clone关键字来实现对象的克隆,比如:

<?php
$obj = new Sample();
$obj2 = clone $obj; // 克隆一个$obj,赋值给$obj2

$obj->foo(); // 输出a=1

$obj2->plus(); //+1
$obj2->foo(); // 输出a=2

$obj->foo(); // 输出a=1,$obj没有发生变化,即$obj与$obj2没有任何联系
?>

当复制完成时, 如果定义了__clone()方法, 则新创建的对象(复制生成的对象)中的__clone()方法会被调用, 可用于修改属性的值(如果有必要的话),比如:

<?php
class Sample {    
    private $a = 1;
    private $instance_no; // instance编号
    static public $all = 0;    // instance总数

    public function foo() {
        echo "a={$this->a}";
    }

    public function bar() {
        echo "instance={$this->instance_no}";
    }

    public function __construct() {
        // 实例号+1
        $this->instance_no = ++self::$all;
    }

    public function __clone() {
        // 实例号+1
        $this->instance_no = ++self::$all;
    }
}

$obj = new Sample();
$obj->foo(); // 输出a=1
$obj->bar(); // 输出instance=1 实例编号为1

$obj2 = clone $obj;
$obj2->foo(); // 输出a=1 没变
$obj2->bar(); // 输出instance=2 实例编号为2
?>

###深浅复制

在PHP中,克隆会把对象的所有属性进行复制,但是如果一个对象中包含引用属性,那么克隆之后,在新生成的对象中这个属性仍然是指向原来变量的引用,这个称为浅复制(shallow copy)。比如:

<?php
class Sample {
    private $a = 1;

    public function plus() {
        $this->a ++;
    }

    public function foo() {
        echo "a={$this->a}";
    }
}

class Sample2 {
    public $b = 1;
    public $obj;

    public function plus() {
        $this->b ++;
    }

    public function foo() {
        echo "b={$this->b}";
    }
}

$obj = new Sample2();
$obj->obj = new Sample();
$obj2 = clone $obj; // clone 

// 修改对象的普通属性
$obj->plus(); // $b + 1
$obj->foo(); // 输出b=2
$obj2->foo(); // 输出b=1,克隆的对象没有发生变化,因此普通属性克隆没有问题

// 修改对象的引用属性
$obj->obj->plus(); // $a+1
$obj->obj->foo(); // 输出a=2
$obj2->obj->foo(); // 输出a=2 克隆对象中的引用属性也发生了变化,这个不是我们想要的结果
?>

以上的列子说明了浅复制导致的问题,我们要自己实现对引用属性的完全复制,术语叫作深复制(deep copy),示例如下:

<?php
class Sample {
    private $a = 1;

    public function plus() {
        $this->a ++;
    }

    public function foo() {
        echo "a={$this->a}";
    }
}

class Sample2 {
    public $b = 1;
    public $obj;

    public function plus() {
        $this->b ++;
    }

    public function foo() {
        echo "b={$this->b}";
    }

    function __clone() {
        $this->obj = clone $this->obj;

        // 当存在对象数组的时候,可以使用以下代码实现深复制
        /*foreach ($this as $key => $val) {
            if (is_object($val) || (is_array($val))) {
                $this->{$key} = unserialize(serialize($val));
            }
        }*/
    }
}

$obj = new Sample2();
$obj->obj = new Sample();
$obj2 = clone $obj; // clone 

// 修改对象的引用属性
$obj->obj->plus(); // $a+1
$obj->obj->foo(); // 输出a=2
$obj2->obj->foo(); // 输出a=1 克隆对象中的引用属性没有发生变化,bingo.
?>

参考:

http://www.php.net/manual/zh/language.oop5.cloning.php

(完)
comments powered by Disqus
Powered by GitHub  &&  Jekyll | CC BY-NC-SA