是這樣的,這個問題確切說應該是這樣的:“ PHP對象的賦值和克隆有什么區別 ”,注意不是復制,就是復制,打開窗子說亮話,就是下面兩行有什么區別。
class User{ public $username = 'test'; } $user1 = new User(); // 注意下面兩行,有啥子區別 $user2 = $user1; $user2 = clone $user1;
$user2 = $user1這種寫法,實際上是引用寫法,也就是說本質上user1和user2變量指向的都是同一個PHP對象,占用的內存也只有一份,如果你修改user2的username屬性實際上就是在user1的username屬性,當然了,修改user1的username屬性也是在修改user2的username屬性,搞來搞去都是同一個。
然而,$user2 = clone $user1這種寫法表示實打實地復制一個User對象出來,而不是普普通通的引用了,這種情況下,你修改user2的username屬性不會影響user1的username屬性,修改user1的任何屬性也不會影響到user2的屬性,說到底是占用了兩份內存。但是,要值得注意的是,你執行完畢clone后系統也並不會真的立馬就開辟一塊兒新的內存用於存放新的對象。如果你clone完這個對象后,並沒有修改、添加、刪除任何屬性或者方法,這兩個對象在內存依然為同一個,只有在你對任意一個對象的屬性或者方法進行增刪改的時候,系統才會真的再開辟一塊兒內存復制對象,這種技術叫做寫實拷貝,是節約內容的一大利器!很多地方都會利用這種技術,大家要記住。
php中有個魔術方法叫做__clone,這個方法呢,只有在你執行clone的時候,會被自動觸發,如果你要對某個類在被克隆的時候做一些特殊操作,那么你就可以在這個類中自定義一些業務邏輯進來,這個具體就要看你的業務場景了。注意,如果你在__clone魔術方法中直接修改類的屬性,他修改的是新復制出來的類的屬性,而不是老類的屬性,用上面的例子就是會修改user2的屬性,而不是user1的屬性,具體代碼如下:
<?php class User{ public $username = 'test'; public function __clone() { $this->username = 'www'; } } $user1 = new User(); // 注意下面兩行,有啥子區別 $user2 = $user1; $user2 = clone $user1;
__clone方法中修改username屬性為www,修改的是user2對象,而不是user1!
$user2 = $user1,就是傳說中的淺復制。
$user2 = clone $user1,就是傳說中的深復制。
然而,深復制依然有特例,比如下面這坨代碼:
<?php class People { public $age = 18; } class User { public $username = 'zhangsan'; public $peple = null; public function __construct() { $this->people = new People(); } } $user1 = new User(); $user2 = clone $user1; $user1->username = 'lisi'; $user1->people->age = 22; echo $user1->username.PHP_EOL; echo $user1->people->age.PHP_EOL; echo $user2->username.PHP_EOL; echo $user2->people->age.PHP_EOL;
結果是啥,你們自己可以運行一下,我只說結果,反正就是user1和user2的username屬性確實是不受影響了,但是people屬性卻是指向的同一個,這個十分尷尬,也就說即便是深復制,當類的某個屬性為另外一個對象的時候,默認只會淺復制這個對象屬性,那么如何徹底地復制對象類型的屬性呢?還得靠__clone魔術方法...
<?php class People { public $age = 18; } class User { public $username = 'zhangsan'; public $peple = null; public function __construct() { $this->people = new People(); } public function __clone() { $this->people = new People(); } } $user1 = new User(); $user2 = clone $user1; $user1->username = 'lisi'; $user1->people->age = 22; echo $user1->username.PHP_EOL; echo $user1->people->age.PHP_EOL; echo $user2->username.PHP_EOL; echo $user2->people->age.PHP_EOL;
運行一下,感受一下結果。
那么,對象的克隆有什么用處呢?
舉個例子吧,比如某個控制器方法中,你通過查詢數據庫返回了一個數據對象為$user,然后你直接echo json_encode( $user )給客戶端,但是在輸出之前呢,你還需要對這個$user進行一些業務邏輯運算,比如對某個屬性加1,然后存入到redis中,但是客戶端又需要的是最原始的從數據庫取出的狀態,那么這個時候,你就只能clone一下這個對象,原對象用於輸出給客戶端,另外一個備份對象用於redis存儲。
在我們公司,也遇到大量的clone,都是用於構建請求對象,和上面這種應用場景是非常類似的,構建出一個原始對象,用於形成最終數據給客戶端,同時克隆一個這個原始對象用於邏輯業務處理生成另外一個結果。