PHP中普通变量的复制可以通过直接赋值完成,比如:
$a = array();
$b = $a;
但是对象无法这么进行复制,仅仅通过赋值传递对象,它们指向的都是同一个对象,修改时也不会发生硬拷贝。比如上面这个例子,我们把$a
赋值给$b
,然后如果我们修改$b
的内容,那么这时候会进行value分离,$a
的内容是不变的,但是如果是把一个对象赋值给了另一个变量,这俩对象不管哪一个修改另外一个都随之改变。
class my_class
{
public $arr = array();
}
$a = new my_class;
$b = $a;
$b->arr[] = 1;
var_dump($a === $b);
====================
输出:bool(true)
还记得我们在《2.1.3.2 写时复制》一节讲过zval有个类型掩码: __type_flag 吗?其中有个是否可复制的标识:IS_TYPE_COPYABLE__ ,copyable的意思是当value发生duplication时是否需要或能够copy,而object的类型是不能复制(不清楚的可以翻下前面的章节),所以我们不能简单的通过赋值语句进行对象的复制。
PHP提供了另外一个关键词来实现对象的复制:clone。
$copy_of_object = clone $object;
clone
出的对象就与原来的对象完全隔离了,各自修改都不会相互影响,另外如果类中定义了__clone()
魔术方法,那么在clone
时将调用此函数。
clone
的实现比较简单,通过zend_object.clone_obj
(即:zend_objects_clone_obj()
)完成。
//zend_objects.c
ZEND_API zend_object *zend_objects_clone_obj(zval *zobject)
{
zend_object *old_object;
zend_object *new_object;
old_object = Z_OBJ_P(zobject);
//重新分配一个zend_object
new_object = zend_objects_new(old_object->ce);
//浅复制properties_table、properties
//如果定义了__clone()则调用此方法
zend_objects_clone_members(new_object, old_object);
return new_object;
}