PHP面向物件_物件克隆clone和魔術方法__clone()
1.物件克隆 clone
PHP4面向物件功能一個很大的缺點,是將物件視為另一種資料型別,這使得很多常見的OOP方法無法使用,如設計模式。這些方法依賴於將物件作為引用傳遞給其他類方法,而不是作為值傳遞,而按值傳遞卻是PHP的預設做法。幸好,PHP5解決了這個問題,現在所有物件在預設情況下都被視為引用。但是,由於所有物件都被視為引用而不是值,所以現在複製物件更為困難。如果嘗試複製一個引用的物件,這隻會指向原物件的地址位置。為了解決複製問題,PHP提供了一種克隆clone(關鍵字,不是方法)物件的顯式方法。
可以在物件前面加clone關鍵字來克隆物件,如下:
destinationObject = clone targetObject;
克隆物件:
class Person{
var $name;
var $sex;
var $age;
function __construct($name, $sex, $age){
$this->name = $name;
$this->sex = $sex;
$this->age = $age;
}
function say(){
echo "我的名字:" . $this->name . ",性別:" . $this->sex . ",年齡:" .$this->age . "<br />";
}
}
$person1 = new Person("張三三", "男", 23);
$person2 = clone $person1; //使用clone關鍵字克隆/複製物件,建立一個物件的副本
$person3 = $person1; //這不是複製物件,而是為物件多複製出一個訪問該物件的引用
$person1->say(); //呼叫原物件中的說話方式,列印原物件中的全部屬性值
$person2->say(); //呼叫副本物件中的說話方式,列印克隆物件中的全部屬性值
$person3->say(); //呼叫原物件中的說話方式,列印原物件中的全部屬性值
2.魔術方法__clone()
在上面的程式中一共建立了兩個物件,其中有一個物件是通過clone關鍵字克隆出來的副本。兩個物件完全能獨立,但他們中的成員及屬性的值完全一樣。如果需要對克隆後的副本物件在克隆時重新為成員屬性賦初值,則可以在類中宣告一個魔術方法“__clone()”。該方法是在物件克隆時自動呼叫的,所以就可以通過此方法對克隆後的副本重新初始化。__clone()方法不需要任何引數。將上例中的程式碼改寫一下,在類中新增魔術方法__clone(),為副本物件中的成員屬性重新初始化。
class Person{
var $name;
var $sex;
var $age;
function __construct($name, $sex, $age){
$this->name = $name;
$this->sex = $sex;
$this->age = $age;
}
function say(){
echo "我的名字:" . $this->name . ",性別:" . $this->sex . ",年齡:" .$this->age . "<br />";
}
function __clone(){
$this->name = "李四四"; //為副本物件中的name屬性重新賦值
$this->age = 10; //為副本物件中的age屬性重新賦值
}
}
$person1 = new Person("張三三", "男", 23);
$person2 = clone $person1; //建立一個物件的副本,並自動呼叫類中的__clone()方法
$person1->say(); //呼叫原物件中的說話方式,列印原物件中的全部屬性值
$person2->say(); //呼叫副本物件中的說話方式,列印克隆物件中的全部屬性值
執行結果:
我的名字:張三三,性別:男,年齡:23
我的名字:李四四,性別:男,年齡:10
3.單例類的加強:禁止克隆
對於一個類的物件,如果使用“clone運算子”,就會複製出一個和當前物件完全一樣的新物件出來,並且,此時還會自動呼叫該類的魔術方法:__clone()(只要該類中有該方法)。
則要實現單例類,就應該對這個單例類的物件“禁止克隆”。在PHP中,為防止對單例類物件的克隆來打破單例類的上述實現形式,通常還為其提供一個空的私有
(private修飾的)__clone()方法。
首先來看“未做禁止克隆”的效果:
class SingetonBasic {
private static $instance; //靜態變數要私有化,防止類外修改
private function __construct() { //建構函式私有化,類外不能直接新建物件
}
//private function __clone() {} //在__clone()前用private修飾,用來禁止克隆
public static function getInstance() { //公共的靜態方法,public——外部的介面,static——不使用物件而是通過類名訪問
if (!(self::$instance instanceof self)) { //私有靜態變數$instance為空
self::$instance = new self(); //新建為自身的物件,並賦值給私有變數$instance
}
return self::$instance; //返回私有變數$instance
}
}
$a = SingetonBasic::getInstance();
$b = SingetonBasic::getInstance();
var_dump($a === $b); //結果為:boolean true a和b指向的是同一個物件
$c = clone $a;
var_dump($a === $c); //結果為:boolean false a和c指向的不是同一個物件
行結果為
boolean true
boolean false
我們“作禁止克隆”處理,即把上面程式碼中的
private function __clone() {} //在__clone()前用private修飾,用來禁止克隆
這行程式碼去掉註釋。
執行結果為
boolean true
Fatal error: Call to private SingetonBasic::__clone()
也就是,在克隆的時候,自動呼叫了__clone(),但是該方法被private修飾,不能再類的外部直接呼叫,結果報錯。