今天在項目中有一個需求,需要複製一份DB類查詢出來的結果作爲原數據和新數據進行比較。
一開始我直接複製了一份:$walletInfoCopy = $walleltInfo;
但是問題是,如果$walletInfo中的值更改,那麼$walletInfoCopy中的值也會更改,後來發現只要實現PHP對象的完全深拷貝就可以實現這個需求。
先說一下深拷貝和淺拷貝通俗理解
- 深拷貝:賦值時值完全複製,完全的copy,對其中一個作出改變,不會影響另一個
- 淺拷貝:賦值時,引用賦值,相當於取了一個別名。對其中一個修改,會影響另一個
PHP中, = 賦值時,普通對象是深拷貝,但對對象來說,是淺拷貝。也就是說,對象的賦值是引用賦值。(對象作爲參數傳遞時,也是引用傳遞,無論函數定義時參數前面是否有&符號)
普通數據類型一般是深拷貝,而對象的深拷貝分爲三種,淺拷貝、部分深拷貝、完全深拷貝,下面一一介紹:
1、普通數據類型的深拷貝 ‘=’
<?php
//普通對象賦值,深拷貝,完全值複製
$m = 1;
$n = $m;
$n = 2;
echo $m;//值複製,對新對象的改變不會對m作出改變,輸出 1.深拷貝
echo PHP_EOL;
?>
2、對象的淺拷貝 ‘=’
//對象賦值,淺拷貝,引用賦值
class Test{
public $a=1;
}
$m = new Test();
$n = $m;//引用賦值
$m->a = 2;//修改m,n也隨之改變
echo $n->a;//輸出2,淺拷貝
echo PHP_EOL;
?>
3、clone實現對象的部分深拷貝
由於對象的賦值時引用,要想實現值複製,php提供了clone函數來實現複製對象。
但是clone函數存在這麼一個問題,克隆對象時,原對象的普通屬性能值複製,但是源對象的對象屬性賦值時還是引用賦值,淺拷貝。
<?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 = $m;//這是完全的淺拷貝,無論普通屬性還是對象屬性
$p = clone $m;
//普通屬性實現了深拷貝,改變普通屬性b,不會對源對象有影響
$p->b = 2;
echo $m->b;//輸出原來的1
echo PHP_EOL;
//對象屬性是淺拷貝,改變對象屬性中的a,源對象m中的對象屬性中a也改變
$p->obj->a = 3;
echo $m->obj->a;//輸出3,隨新對象改變
?>
要想實現對象真正的深拷貝,有下面兩種方法:
寫clone函數:如下
<?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;
}
}
$m = new TestOne();
$n = clone $m;
$n->b = 2;
echo $m->b;//輸出原來的1
echo PHP_EOL;
//可以看到,普通屬性實現了深拷貝,改變普通屬性b,不會對源對象有影響
//由於改寫了clone函數,現在對象屬性也實現了真正的深拷貝,對新對象的改變,不會影響源對象
$n->obj->a = 3;
echo $m->obj->a;//輸出1,不隨新對象改變,還是保持了原來的屬性
?>
第二種方法,利用序列化反序列化實現,這種方法實現對象的深拷貝簡單,不需要修改類 改寫__clone()函數不太方便,而且你得在每個類中把這個類裏面的對象屬性都在__clone()中 一一 clone
<?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,實現賦值