php在面向對象部分有很多相關的魔術方法,這些方法為面向對象實現提供了便利,本文將詳細介紹魔術方法
大多數類都有一種稱為構造函數的特殊方法。當創建一個對象時,它將自動調用構造函數,通常用它執行一些有用的初始化任務
構造函數的聲明與其它操作的聲明一樣,只是其名稱必須是兩個下劃線__construct( )。這是PHP5中的變化;PHP4的版本中,構造函數的名稱必須與類名相同。為了向下兼容,如果一個類中沒有名為__construct( )的方法,PHP將搜索一個與類名相同的方法
void __construct ([ mixed $args [, $... ]] )
如果子類中定義了構造函數則不會隱式調用其父類的構造函數。要執行父類的構造函數,需要在子類的構造函數中調用 parent::__construct()。如果子類沒有定義構造函數則會如同一個普通的類方法一樣從父類繼承(假如沒有被定義為 private 的話)
<?php class BaseClass { function __construct() { print "In BaseClass constructor\n"; } } class SubClass extends BaseClass { function __construct() { parent::__construct(); print "In SubClass constructor\n"; } } class OtherSubClass extends BaseClass { } // In BaseClass constructor $obj = new BaseClass(); // In BaseClass constructor // In SubClass constructor $obj = new SubClass(); // In BaseClass constructor $obj = new OtherSubClass(); ?>
與構造方法相對的就是析構方法。析構方法是PHP5新添加的內容,在PHP4中沒有析構方法。析構方法是在對象被銷毀之前自動調用的方法,主要執行一些特定的操作,例如關閉文件,釋放結果集等
與構造方法類似,一個類的析構方法名稱必須是兩個下劃線 _ _destruct( )。析構函數不能帶有任何參數
<?php class MyDestructableClass { function __construct() { print "In constructor\n"; $this->name = "MyDestructableClass"; } function __destruct() { print "Destroying " . $this->name . "\n"; } } //In constructor Destroying MyDestructableClass $obj = new MyDestructableClass(); ?>
get()
讀取不可訪問屬性(protected、private)時,__get()會被調用,並將屬性名以第一個參數(string)傳進此方法中
public mixed __get ( string $name )
<?php class demo{ protected $protected = 1; public $public = 2; private $private = 3; function __get($name){ echo "111{$name}111<br>"; } } $d1 = new demo; $d1->protected;//111protected111 $d1->public; $d1->private;//111private111 ?>
set()
在給不可訪問屬性(protected、private)賦值時,__set() 會被調用,並將屬性名以第一個參數(string),值作為第二參數(mixed)傳進此方法中
public void __set ( string $name , mixed $value )
<?php class demo{ protected $protected = 1; public $public = 2; private $private = 3; function __set($name,$value){ echo "0{$name}0{$value}<br>"; } } $d1 = new demo; $d1->protected = '1';//0protected01 $d1->public = '2'; $d1->private = '3';//0private03 ?>
isset()
當對不可訪問屬性(protected、private)調用 isset() 或 empty() 時,__isset() 會被調用
public bool __isset ( string $name )
<?php class demo{ protected $protected = 1; public $public = 2; private $private = 3; function __isset($name){ echo "0{$name}0<br>"; } } $d1 = new demo; empty($d1->protected);//0protected0 empty($d1->public); empty($d1->private);//0private0 ?>
unset()
當對不可訪問屬性(protected、private)調用unset()時,__unset()會被調用
public void __unset ( string $name )
<?php class demo{ protected $protected = 1; public $public = 2; private $private = 3; function __unset($name){ echo "0{$name}0<br>"; } } $d1 = new demo; unset($d1->protected);//0protected0 unset($d1->public); unset($d1->private);//0private0 ?>
clone()
在對象克隆時會自動調用clone()方法,這方法不需要任何參數,可以通過該方法對克隆後的副本重新初始化
clone()方法會自動包含this和that兩個對象的引用,this是副本對象的引用,that是原本對象的引用
<?php class Person{ private $name; private $sex; private $age; function __construct($name="",$sex="",$age=1){ $this->name= $name; $this->sex = $sex; $this->age = $age; } function __clone(){ $this->name = $this->name."的副本"; } function say(){ echo "我的名字:" .$this->name.",性別:".$this->sex.",年齡:".$this->age."<br>"; } } $p1 = new Person('張三','男','20'); $p2 = clone $p1; $p1->say();//我的名字:張三,性別:男,年齡:20 $p2->say();//我的名字:張三的副本,性別:男,年齡:20 ?>
toString()
__toString()方法用於一個類被當成字符串時應怎樣回應,它是快速獲取對象的字符串表示的最便捷的方式,是直接輸出對象引用時自動調用的方法
<?php class TestClass { public $foo; public function __construct($foo) { $this->foo = $foo; } public function __toString() { return $this->foo; } } $class = new TestClass('Hello'); echo $class;//Hello ?>
call()
在對象中調用一個不可訪問方法時,__call()會被調用
callStatic()
在靜態上下文中調用一個不可訪問方法時,__callStatic()會被調用
<?php class MethodTest { public function __call($name, $arguments) { echo "Calling object method '$name' " . implode(', ', $arguments). "\n"; } public static function __callStatic($name, $arguments) { echo "Calling static method '$name' " . implode(', ', $arguments). "\n"; } } $obj = new MethodTest; //Calling object method 'runTest' in object context $obj->runTest('in object context'); //Calling static method 'runTest' in static context MethodTest::runTest('in static context'); ?>
autoload()
在PHP5中,可以定義一個__autoload()函數,它會在試圖使用尚未被定義的類時自動調用。通過調用此函數,腳本引擎在PHP出錯失敗前有了最後一個機會加載所需的類
<?php function __autoload($class_name) { require_once $class_name . '.php'; } $obj = new MyClass1(); $obj2 = new MyClass2(); ?>
sleep()
在調用serialize()函數將對象串行化時,檢查類中是否存在一個魔術方法 __sleep()。如果存在,該方法會先被調用,然後才執行序列化操作。此功能可以用於清理對象,並返回一個包含對象中所有應被序列化的變量名稱的數組。如果該方法未返回任何內容,則 NULL 被序列化,並產生一個 E_NOTICE 級別的錯誤
__sleep()函數不需要接受任何參數,但需要返回一個數組,在數組中包含需要串行化的屬性。未被包含在數組中的屬性將在串行化時被忽略。如果沒有在類中聲明__sleep()方法,對象中的所有屬性都將被串行化
wakeup()
在調用unserialize()函數將對象反串行化對象時,則會自動調用對象中的__wakeup()方法,用來在二進制串重新組成一個對象時,為新對象中的成員屬性重新初始化
wakeup()經常用在反序列化操作中,例如重新建立數據庫連接,或執行其它初始化操作
<?php class Connection { protected $link; private $server, $username, $password, $db; public function __construct($server, $username, $password, $db) { $this->server = $server; $this->username = $username; $this->password = $password; $this->db = $db; $this->connect(); } private function connect() { $this->link = mysql_connect($this->server, $this->username, $this->password); mysql_select_db($this->db, $this->link); } public function __sleep() { return array('server', 'username', 'password', 'db'); } public function __wakeup() { $this->connect(); } } ?>
invoke()
當嘗試以調用函數的方式調用一個對象時,__invoke()方法會被自動調用
<?php class CallableClass { function __invoke($x) { var_dump($x); } } $obj = new CallableClass; $obj(5);//int(5) var_dump(is_callable($obj));//bool(true) ?>
【補充】
set_state()
當調用var_export()導出類時,set_state()方法會被調用,本方法的唯一參數是一個數組,其中包含按 array('property' => value, ...) 格式排列的類屬性
[注意]var_export()返回關於傳遞給該函數的變量的結構信息,它和var_dump()類似,不同的是其返回的表示是合法的PHP代碼,也就是說,var_export返回的代碼,可以直接當作php代碼賦給一個變量。 而這個變量就會取得和被var_export一樣的類型的值
<?php class A { public $var1; public $var2; public static function __set_state($an_array) { $obj = new A; $obj->var1 = $an_array['var1']; $obj->var2 = $an_array['var2']; return $obj; } } $a = new A; $a->var1 = 5; $a->var2 = 'foo'; eval('$b = ' . var_export($a, true) . ';'); /* object(A)[2] public 'var1' => int 5 public 'var2' => string 'foo' (length=3) */ var_dump($b); ?>