天天看點

PHP中的12個魔術方法

PHP中的12個魔術方法

這個标題有點牽強因為php有不隻12種魔術方法, 但是這些将會引導你使用php魔術方法一個好的開始。它可能魔幻,但是并不需要魔杖。

PHP中有一些特殊的函數和方法,這些函數和方法相比普通方法的特殊之處在于: 使用者代碼通常不會主動調用, 而是在特定的時機會被PHP自動調用(這些方法在php特定事件下将會被觸發)。這些'魔術'方法擁有者特殊的名字,在PHP中通常以"__"打頭的方法都作為魔術方法, 是以通常不要定義以"__"開頭的函數或方法。

__construct 

構造器是一個魔術方法,當對象被執行個體化時它會被調用。在一個類聲明時它常常是第一件做的事但是沒得必要他也像其他任何方法在類中任何地方都可以聲明,構造器也能像其他方法樣繼承。如果我們想到以前繼承例子從介紹到oop,我們能添加構造方法到Animal 類中,如:

class Animal{
  public function __construct(){
    $this->created = time();
    $this->logfile_handle = fopen('/tmp/log.txt', 'w');
  }
}           

複制

現在我們建立一個類來繼承Animal類 - Penguin類!不添加任何屬性和方法在Penguin類中,我們能申明并定義它繼承自Animal類,如: 

class Penguin extends Animal{
  
}
$tux = new Penguin;
echo $tux->created;           

複制

如果我們定義一個構造方法在Penguin類中,然後Penguin對象将會運作當它被執行個體化後。由于并沒有構造方法,PHP 會參考父類方法定義 資訊來使用它是以我們能覆寫父類方法,或者不,在我們的新類中-很便利。

__destruct

你發現檔案句柄也是構造器一部分嗎?當我們使用完一個對象時真不想把事情放一邊,是以析構方法做着與構造方法相反的事情。當對象被銷毀時,析構方法會運作,或者明确的說當我們不再使用它時,php會為我們清理掉。Animal類中,我們的析構方法像這樣,如:

class Animal{
  public function __construct(){
    $this->created = time();
    $this->logfile_handle = fopen('/tmp/log.txt', 'w');
  }
  public function __destruct(){
    fclose($this->logfile_handle);
  }
}           

複制

析構器讓我們關閉任何額外的資源比如被使用過的對象。在php中由于我們有這樣運作時間短的腳本(留意在更新的php版本中增強的垃圾回收機制),通常讨論記憶體溢出根本不需要。然而它仍是好的推行方法來清理而且總體上讓程式運作起來更高效。 

__get

這個魔術方法是一個非常靈巧的小技巧 - 它使實際上不存在的屬性如同存在一半。讓我們舉個小企鵝的例子:

class Penguin extends Animal {
  public function __construct($id) {
    $this->getPenguinFromDb($id);
  }
  public function getPenguinFromDb($id) {
    // elegant and robust database code goes here
  }
}           

複制

現在,如果我們的小企鵝有一個 "name" 屬性,而在此之後加載的屬性為 "age",那麼我們可以這樣處理:

$tux = new Penguin(3);
echo $tux->name . " is " . $tux->age . " years old\n";           

複制

然而,設想一下,後端資料庫或資料供應者發生了改變,"name"沒有了,變味了"username"。并且設想這是一個非常複雜的應用,而需要修改的調用"name"的地方非常多。我們可以使用 __get 方法,使得"name"屬性如同存在一樣:

class Penguin extends Animal {
  public function __construct($id) {
    $this->getPenguinFromDb($id);
  }
  public function getPenguinFromDb($id) {
    // elegant and robust database code goes here
  }
  public function __get($field) {
    if($field == 'name') {
      return $this->username;
    }
}           

複制

這并不是編寫整個系統的好方法,因為它會讓調試工作變得更困難,但它是一個非常有價值的工具。它允許如同屬性一樣使用或者展示需要經過計算的資料,以及無數我都想不到的地方。

__set

那麼,我們将所有對 $this->name 的調用都更改為傳回 $this->username的值,那麼,如果我們想要設定這個值呢?也許我們有一個賬戶界面允許使用者修改他們的名字。這時我們就需要 __set 方法的幫助了,舉例說明:

class Penguin extends Animal {
  public function __construct($id) {
    $this->getPenguinFromDb($id);
  }
  public function getPenguinFromDb($id) {
    // elegant and robust database code goes here
  }
  public function __get($field) {
    if($field == 'name') {
      return $this->username;
    }
  }
  public function __set($field, $value) {
    if($field == 'name') {
      $this->username = $value;
    }
  }
}           

複制

這樣,我們就針對大量的調用僞造對象的屬性,正如我說的,這并不是一個正統的方法,但卻是一個很有用的技巧,值得記住。

__call

這裡有兩種近似的方法,我并沒有單獨列出來,而是一起說明。一個是 _call 方法,如果定義,它将在調用未定義過的方法時被調用;另一個是 _callStatic 方法,工作方式與第一個相同,但卻是在調用未定義的靜态方法時生效(PHP 5.3 加入).通常我使用 __call 進行友善的錯誤處理,這在需要别人整合調用你的方法的庫代碼中非常有用。例如,如果一段腳本擁有一個企鵝對象,名為 $penguin ,它包含一個 $penguin->speak() 方法...假設 speak() 方法沒有定義,那麼正常情況下我們會看到:

PHP Fatal error: Call to undefined method Penguin::speak() in ...            

複制

通過定義 __call 方法,我們可以使用一些更友善的提示資訊來代替 PHP 的錯誤提示:

class Animal {
}
class Penguin extends Animal {
  public function __construct($id) {
    $this->getPenguinFromDb($id);
  }
  public function getPenguinFromDb($id) {
    // elegant and robust database code goes here
  }
  public function __get($field) {
    if($field == 'name') {
      return $this->username;
    }
  }
  public function __set($field, $value) {
    if($field == 'name') {
      $this->username = $value;
    }
  }
  public function __call($method, $args) {
      echo "unknown method " . $method;
      return false;
  }
}           

複制

這将捕獲的錯誤并回應。在實際應用中,更合适的方法是依據你的需要紀錄消息日志·,将使用者重定向,或者抛出一個異常,但概念是相同的。在這裡你可以處理任何你需要處理的不當調用,你可以檢測方法的名稱,并一一處理——例如,你可以同上面我們重命名屬性一些樣重命名方法。

__sleep

__sleep()方法會被調用當對象被序列化後,并允許你處理序列化。這有各種各樣的程式,一個很好的例子如果一個對象包含某種類型的指針,例如檔案句柄或引用另一個對象。當對象被序列化然後解序列化,這些引用類型是無用的,因為這些類型的引用的目标可能不再存在或有效。是以,最好是來取消這些資訊在存儲它們之前。

__wakeup

__wakeup()是與__sleep()方法相反的,允許您更改對象解序列化的行為。和__sleep()一起使用,可以用來恢複被删除的句柄和對象當對象被序列化時。一個很好的例子程式是資料庫句柄被取消設定當該項被序列化,然後恢複到目前配置中設定項目時,解序列化一個資料庫句柄。

__clone

我們看過一個使用clone關鍵字的例子,在我的介紹從入門到oop的第二部分,建立對象的副本,而不是有兩個變量指向同一個實際的資料。在一個類中重寫此方法,我們可以觀察發生了什麼當在對象上使用clone關鍵字時,。雖然這是不是我們每一天能遇到的,一個漂亮的用例是建立一個真正的單例模式通過添加private通路修飾符給這個方法。

__toString

無疑把最好的始終留到最後,__toString方法是一個非常友善的附加方法對于我們的工具包。該方法可以聲明覆寫對象的行為,當作為一個字元串輸出時,例如,當它被輸出時。如果你想能輸出對象到模闆中,你可以使用此方法來控制輸出結果。讓我們再來看看在Penguin類中: 

class Penguin {
  public function __construct($name) {
      $this->species = 'Penguin';
      $this->name = $name;
  }
  public function __toString() {
      return $this->name . " (" . $this->species . ")\n";
  }
}           

複制

在适當的位置,輸出該對象通過調用echo輸出它,如: 

$tux = new Penguin('tux');
echo $tux;            

複制

我不常常使用這種捷徑,但是知道它的存在是很有用的。 

更多魔術方法

10、__invoke(PHP 5.3.0以上版本有效)

當嘗試以調用函數的方式調用一個對象時,__invoke 方法會被自動調用。

11、__callStatic(PHP 5.3.0以上版本有效)

它的工作方式類似于__call() 魔術方法,__callStatic() 是為了處理靜态方法調用。

PHP 确實加強了對 __callStatic() 方法的定義;它必須是公共的,并且必須被聲明為靜态的。同樣,__call() 魔術方法必須被定義為公共的,所有其他魔術方法都必須如此...

12.__debuginfo

當調用var_dump()列印對象時被調用(當你不想列印所有屬性)适用于PHP5.6版本

更多參考 http://php.net/manual/zh/language.oop5.magic.php#object.invoke , 列出的所有的魔術方法(是的。不僅僅是文中所列出的,我僅僅選出那些我認為最好開始學習的)如果你想了解其他請仔細檢視...