PHP对象深拷贝与浅拷贝

php对象的赋值是引用赋值,可以参考之前一篇文章PHP之道–对象和引用;

深拷贝:赋值完全复制,两个对象完全独立,其中一个对象的属性做出改变时不会影响到另一个。

浅拷贝:引用赋值,相对于取了一个别名,其中一个对象属性做出修改会影响到另一个。

对象的调用

1
2
3
4
5
6
7
8
9
class a{
var $abc="ABC";
}
$b=new a;
$c=$b;
echo $b->abc;//这里输出ABC
echo $c->abc;//这里输出ABC
$b->abc="DEF";
echo $c->abc;//这里输出DEF

对象赋值,浅拷贝。上列中$b=new a; $c=$b; 其实等效于$b=new a; $c=&$b,也就说取了另一个名字而已,指向的内存空间还是一样的。如果想要完全独立出来一个对象,互不影响,这就涉及到clone深拷贝

Clone函数

clone函数在克隆对象时候,普通属性是深拷贝,但原对象的对象属性还是引用赋值,浅拷贝,看代码理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Test {
public $a = 1;
}

class TestOne {
public $b = 1; //这里就是普通属性
public $obj;

public function __construct(){
$this->obj = new Test(); //这个obj就是对象属性,它是一个Test的实例
}
}

$old = new TestOne();
$old2 = $old; //这是完全浅拷贝,和上面对象调用那个例子一样的
$new = clone $old; //这里使用了clone函数,普通属性不会被影响,而对象属性会被影响

$new->b = 2; //改变一下普通属性
echo $old->b; //输出原来的1;说明普通属性是深拷贝

$new->obj->a = 3;
echo $old->obj->a; //输出3,说明对象属性是浅拷贝,还是会随着新对象而改变

魔术方法__clone实现真正深拷贝

为了实现全部属性的深拷贝,可以使用魔术方法__clone

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
class Test{
public $a=1;
}

class TestOne{
public $b=1;
public $obj;
//包含了一个对象属性,clone时,它会是浅拷贝
public function __construct(){
$this->obj = new Test();
}

//方法一:重写clone函数
public function __clone(){
$this->obj = clone $this->obj;
}
}

$old = new TestOne();
$new = clone $old;

$new->b = 2;
echo $old->b;//输出原来的1
echo PHP_EOL;
//可以看到,普通属性实现了深拷贝,改变普通属性b,不会对源对象有影响

//由于改写了clone函数,现在对象属性也实现了真正的深拷贝,对新对象的改变,不会影响源对象
$new->obj->a = 3;
echo $old->obj->a;//输出1,不随新对象改变,还是保持了原来的属性

?>

序列化反序列化

如果每次都要去类里面修改__clone会很不方便,而且还必须把所有对象属性都放在__clone里面保证全部深拷贝。可以使用serialize序列化,unserialize反序列化实现对象深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
class Test{
public $a=1;
}

class TestOne{
public $b=1;
public $obj;
//包含了一个对象属性,clone时,它会是浅拷贝
public function __construct(){
$this->obj = new Test();
}

}

$m = new TestOne();
//方法二,序列化反序列化实现对象深拷贝
$n = serialize($m);
$n = unserialize($n);

$n->b = 2;
echo $m->b;//输出原来的1
echo PHP_EOL;
//可以看到,普通属性实现了深拷贝,改变普通属性b,不会对源对象有影响


$n->obj->a = 3;
echo $m->obj->a;//输出1,不随新对象改变,还是保持了原来的属性,可以看到,序列化和反序列化可以实现对象的深拷贝

?>

json_encode之后再json_decode,实现赋值也能达到同样效果

原文参考:[PHP中对象的深拷贝与浅拷贝];