php語言

當前位置 /首頁/計算機/php語言/列表

PHP中的魔術方法詳解

在PHP的學習中,以兩個下劃線__開頭的方法稱為魔術方法,下面小編就他們的用法整理出以下文章,希望對大家學習PHP有幫助,更多內容請關注應屆畢業生網!

PHP中的魔術方法詳解
  前言

PHP中把以兩個下劃線__開頭的方法稱為魔術方法(Magic methods),這些方法在PHP中充當了舉足輕重的作用。 魔術方法包括:

__sleep(),執行serialize()時,先會呼叫這個函式

__wakeup(),執行unserialize()時,先會呼叫這個函式

__toString(),類被當成字串時的迴應方法

__invoke(),呼叫函式的方式呼叫一個物件時的迴應方法

__set_state(),呼叫var_export()匯出類時,此靜態方法會被呼叫。

__clone(),當物件複製完成時呼叫

__autoload(),嘗試載入未定義的類

__debugInfo(),列印所需除錯資訊

  範例

下面讓我們以例項的形式向大家講解下這幾個魔術方法時如何使用的。

範例

  九、 __sleep(),執行serialize()時,先會呼叫這個函式

serialize() 函式會檢查類中是否存在一個魔術方法 __sleep()。如果存在,則該方法會優先被呼叫,然後才執行序列化操作。

此功能可以用於清理物件,並返回一個包含物件中所有應被序列化的變數名稱的陣列。

如果該方法未返回任何內容,則 NULL 被序列化,併產生一個 E_NOTICE 級別的錯誤。

注意:

__sleep() 不能返回父類的私有成員的名字。這樣做會產生一個 E_NOTICE 級別的錯誤。可以用 Serializable 介面來替代。

作用:

__sleep() 方法常用於提交未提交的資料,或類似的清理操作。同時,如果有一些很大的物件,但不需要全部儲存,這個功能就很好用。

具體請參考如下程式碼:

12345678<?phpclassPerson{public$sex;private$name;private$age;publicfunction__construct($name="",$age=25,$sex='男'){$this->name=$name;$this->age=$age;$this->sex=$sex;}/***@param$content**@returnbool*/publicfunction__isset($content){echo"當在類外部使用isset()函式測定私有成員{$content}時,自動呼叫<br>";echoisset($this->$content);}}$person=newPerson("小明",25);//初始賦值echoisset($person->sex),"<br>";echoisset($person->name),"<br>";echoisset($person->age),"<br>";

程式碼執行結果:

當在類外部使用serialize()時會呼叫這裡的__sleep()方法

O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}

十、 __wakeup(),執行unserialize()時,先會呼叫這個函式

如果說 __sleep() 是白的,那麼 __wakeup() 就是黑的了。

那麼為什麼呢?

因為:

與之相反,`unserialize()` 會檢查是否存在一個 `__wakeup()` 方法。如果存在,則會先呼叫 `__wakeup` 方法,預先準備物件需要的資源。

作用:

__wakeup() 經常用在反序列化操作中,例如重新建立資料庫連線,或執行其它初始化操作。

還是看程式碼:

12345678<?phpclassPerson{public$sex;private$name;private$age;publicfunction__construct($name="",$age=25,$sex='男'){$this->name=$name;$this->age=$age;$this->sex=$sex;}/***@param$content**@returnbool*/publicfunction__isset($content){echo"當在類外部使用isset()函式測定私有成員{$content}時,自動呼叫<br>";echoisset($this->$content);}}$person=newPerson("小明",25);//初始賦值echoisset($person->sex),"<br>";echoisset($person->name),"<br>";echoisset($person->age),"<br>";

當在類外部使用serialize()時會呼叫這裡的.__sleep()方法

string(58) "O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}" 當在類外部使用serialize()時會呼叫這裡的__sleep()方法 當在類外部使用unserialize()時會呼叫這裡的__wakeup()方法 object(Person)#2 (3) { ["sex"]=> string(3) "男" ["name"]=> int(2) ["age"]=> int(25) }

 十一、 __toString(),類被當成字串時的迴應方法  作用:

__toString() 方法用於一個類被當成字串時應怎樣迴應。例如 `echo $obj;` 應該顯示些什麼。

注意:

此方法必須返回一個字串,否則將發出一條 `E_RECOVERABLE_ERROR` 級別的致命錯誤。

警告:

不能在 __toString() 方法中丟擲異常。這麼做會導致致命錯誤。

程式碼:

12345678<?phpclassPerson{public$sex;public$name;public$age;publicfunction__construct($name="",$age=25,$sex='男'){$this->name=$name;$this->age=$age;$this->sex=$sex;}publicfunction__toString(){return'gogogo';}}$person=newPerson('小明');//初始賦值echo$person;

結果:

go go go

那麼如果類中沒有 __toString() 這個魔術方法執行會發生什麼呢?讓我們來測試下:

程式碼:

12345678<?phpclassPerson{public$sex;public$name;public$age;publicfunction__construct($name="",$age=25,$sex='男'){$this->name=$name;$this->age=$age;$this->sex=$sex;}}$person=newPerson('小明');//初始賦值echo$person;

結果:

Catchable fatal error: Object of class Person could not be converted to string in D:phpStudyWWWtest on line 18

很明顯,頁面報了一個致命錯誤,這是語法所不允許的。

 十二、 __invoke(),呼叫函式的方式呼叫一個物件時的迴應方法

作用:

當嘗試以呼叫函式的方式呼叫一個物件時,__invoke() 方法會被自動呼叫。

注意:

本特性只在 PHP 5.3.0 及以上版本有效。

直接上程式碼:

123456789<?phpclassPerson{public$sex;public$name;public$age;publicfunction__construct($name="",$age=25,$sex='男'){$this->name=$name;$this->age=$age;$this->sex=$sex;}publicfunction__invoke(){echo'這可是一個物件哦';}}$person=newPerson('小明');//初始賦值$person();

結果:

Catchable fatal error: Object of class Person could not be converted to string in D:phpStudyWWWtest on line 18

很明顯,頁面報了一個致命錯誤,這是語法所不允許的。

 十二、 __invoke(),呼叫函式的方式呼叫一個物件時的迴應方法

作用:

當嘗試以呼叫函式的方式呼叫一個物件時,__invoke() 方法會被自動呼叫。

注意:

本特性只在 PHP 5.3.0 及以上版本有效。

直接上程式碼:

123456789<?phpclassPerson{public$sex;public$name;public$age;publicfunction__construct($name="",$age=25,$sex='男'){$this->name=$name;$this->age=$age;$this->sex=$sex;}publicfunction__invoke(){echo'這可是一個物件哦';}}$person=newPerson('小明');//初始賦值$person();

檢視執行結果:

這可是一個物件哦

當然,如果你執意要將物件當函式方法使用,那麼會得到下面結果:

Fatal error: Function name must be a string in D:phpStudyWWWtest online 18

 十三、 __set_state(),呼叫var_export()匯出類時,此靜態方法會被呼叫。

作用:

自 PHP 5.1.0 起,當呼叫 var_export() 匯出類時,此靜態方法會被自動呼叫。

引數:

本方法的唯一引數是一個數組,其中包含按 array(‘property’ => value, …) 格式排列的類屬性。

下面我們先來看看在沒有加 __set_state() 情況按下,程式碼及執行結果如何:

上程式碼:

123456789<?phpclassPerson{public$sex;public$name;public$age;publicfunction__construct($name="",$age=25,$sex='男'){$this->name=$name;$this->age=$age;$this->sex=$sex;}}$person=newPerson('小明');//初始賦值var_export($person);

看結果:

Person::__set_state(array( 'sex' => '男', 'name' => '小明', 'age' => 25, ))

很明顯,將物件中的屬性都打印出來了

加了 __set_state() 之後:

繼續上程式碼:

123456789<?phpclassPerson{public$sex;public$name;public$age;publicfunction__construct($name="",$age=25,$sex='男'){$this->name=$name;$this->age=$age;$this->sex=$sex;}publicstaticfunction__set_state($an_array){$a=newPerson();$a->name=$an_array['name'];return$a;}}$person=newPerson('小明');//初始賦值$person->name='小紅';var_export($person);

繼續看結果:

Person::__set_state(array( 'sex' => '男', 'name' => '小紅', 'age' => 25, ))

十四、 __clone(),當物件複製完成時呼叫

在多數情況下,我們並不需要完全複製一個物件來獲得其中屬性。但有一個情況下確實需要:如果你有一個 GTK 視窗物件,該物件持有視窗相關的資源。你可能會想複製一個新的視窗,保持所有屬性與原來的視窗相同,但必須是一個新的物件(因為如果不是新的物件,那麼一個視窗中的改變就會影響到另一個視窗)。還有一種情況:如果物件 A 中儲存著物件 B 的引用,當你複製物件 A 時,你想其中使用的物件不再是物件 B 而是 B 的一個副本,那麼你必須得到物件 A 的一個副本。

作用:

物件複製可以通過 clone 關鍵字來完成(如果可能,這將呼叫物件的 __clone() 方法)。物件中的 __clone() 方法不能被直接呼叫。

語法:

$copy_of_object = clone $object;

注意:

當物件被複制後,PHP 5 會對物件的所有屬性執行一個淺複製(shallow copy)。所有的引用屬性 仍然會是一個指向原來的變數的引用。

當複製完成時,如果定義了 __clone() 方法,則新建立的物件(複製生成的物件)中的 __clone() 方法會被呼叫,可用於修改屬性的值(如果有必要的話)。

看程式碼:

123456789<?phpclassPerson{public$sex;public$name;public$age;publicfunction__construct($name="",$age=25,$sex='男'){$this->name=$name;$this->age=$age;$this->sex=$sex;}publicfunction__clone(){echo__METHOD__."你正在克隆物件<br>";}}$person=newPerson('小明');//初始賦值$person2=clone$person;var_dump('persion1:');var_dump($person);echo'<br>';var_dump('persion2:');var_dump($person2);

看結果:

Person::__clone你正在克隆物件

string(9) "persion1:" object(Person)#1 (3) { ["sex"]=> string(3) "男" ["name"]=> string(6) "小明" ["age"]=> int(25) } string(9) "persion2:" object(Person)#2 (3) { ["sex"]=> string(3) "男" ["name"]=> string(6) "小明" ["age"]=> int(25) }

克隆成功。

十五、__autoload(),嘗試載入未定義的類

作用:

你可以通過定義這個函式來啟用類的自動載入。

在魔術函式 __autoload() 方法出現以前,如果你要在一個程式檔案中例項化100個物件,那麼你必須用include或者require包含進來100個類檔案,或者你把這100個類定義在同一個類檔案中 —— 相信這個檔案一定會非常大,然後你就痛苦了。

但是有了 __autoload() 方法,以後就不必為此大傷腦筋了,這個類會在你例項化物件之前自動載入制定的檔案。

還是通過例子來看看吧:

先看看以往的方式:

123456789101112131415161718/***檔案non_*/require_once('project/class/');require_once('project/class/');require_once('project/class/');if(條件A){$a=newA();$b=newB();$c=newC();//…業務邏輯}elseif(條件B){$a=newA();$b=newB();//…業務邏輯}

看到了嗎?不用100個,只是3個看起來就有點煩了。而且這樣就會有一個問題:如果指令碼執行“條件B”這個分支時,這個檔案其實沒有必要包含。因為,任何一個被包含的檔案,無論是否使用,均會被php引擎編譯。如果不使用,卻被編譯,這樣可以被視作一種資源浪費。更進一步,如果包含了,包含了。並且大部分情況都執行“條件B”分支,那麼就會浪費一部分資源去編譯,,三個“無用”的檔案。

那麼如果使用 __autoload() 方式呢?

1234567891011121314151617181920/***檔案autoload_*/function__autoload($className){$filePath=“project/class/{$className}”;if(is_readable($filePath)){require($filePath);}}if(條件A){$a=newA();$b=newB();$c=newC();//…業務邏輯}elseif(條件B){$a=newA();$b=newB();//…業務邏輯}

ok,不論效率怎麼用,最起碼介面看起來舒服多了,沒有太多冗餘的代。

再來看看這裡的效率如何,我們分析下:

當php引擎第一次使用類A,但是找不到時,會自動呼叫 __autoload 方法,並將類名“A”作為引數傳入。所以,我們在 __autoload() 中需要的做的就是根據類名,找到相應的檔案,幷包含進來,如果我們的方法也找不到,那麼php引擎就會報錯了。

注意:

這裡可以只用require,因為一旦包含進來後,php引擎再遇到類A時,將不會呼叫__autoload,而是直接使用記憶體中的類A,不會導致多次包含。

擴充套件:

其實php發展到今天,已經有將 `spl_autoload_register` — 註冊給定的函式作為 __autoload 的實現了,但是這個不在啊本文講解之內,有興趣可以自行看手冊。

十六、__debugInfo(),列印所需除錯資訊

注意:

該方法在PHP 5.6.0及其以上版本才可以用,如果你發現使用無效或者報錯,請檢視啊你的版本。

看程式碼:

12345678910111213141516171819<?phpclassC{private$prop;publicfunction__construct($val){$this->prop=$val;}/***@returnarray*/publicfunction__debugInfo(){return['propSquared'=>$this->prop**2,];}}var_dump(newC(42));

結果:

object(C)#1 (1) { ["propSquared"]=> int(1764) }

再次注意:

這裡的 `**` 是乘方的意思,也是在PHP5.6.0及其以上才可以使用,詳情請檢視PHP手冊

總結

以上就是PHP中我瞭解到的魔術方法了,常用的包括 __set() __get() __autoload() 等應該熟悉,其他的瞭解也沒有關係,畢竟知識不怕多嘛。

TAG標籤:PHP #