首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 魔術方法完整圖鑑:從 __construct 到 __callStatic 深度解析

     編輯:PHP 綜合知識

魔術方法總覽

PHP 保留雙底線 __ 開頭的方法名為魔術方法,共 16 種 (含 __invoke/__callStatic),觸發時機各異,核心用途在於物件生命週期攔截、屬性/方法動態存取、序列化控制等。

分類詳細解析

1. 屬性存取攔截:__get / __set / __isset / __unset

class PropertyInterceptor {
    private $data = [];
    
    public function __get($name) {
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        }
        trigger_error("屬性 '$name' 不存在", E_USER_NOTICE);
        return null;
    }
    
    public function __set($name, $value) {
        $this->data[$name] = $value;
    }
    
    public function __isset($name) {
        return isset($this->data[$name]);
    }
    
    public function __unset($name) {
        unset($this->data[$name]);
    }
}

$obj = new PropertyInterceptor();
$obj->name = "測試";      // 觸發 __set
echo $obj->name;          // 觸發 __get
isset($obj->name);        // 觸發 __isset
unset($obj->name);        // 觸發 __unset

注意: 這四個方法只對「未宣告」或「不可見」(private/protected) 屬性生效。

2. 方法呼叫攔截:__call / __callStatic

class MethodInterceptor {
    public function __call($method, $args) {
        echo "實例方法 '$method' 不存在,參數: " . json_encode($args) . "\n";
    }
    
    public static function __callStatic($method, $args) {
        echo "靜態方法 '$method' 不存在,參數: " . json_encode($args) . "\n";
    }
}

$obj = new MethodInterceptor();
$obj->undefinedMethod(1, 2, 3);     // 觸發 __call
MethodInterceptor::staticUndefined('a', 'b'); // 觸發 __callStatic (PHP 5.3+)

規則: 先找本類方法 → 父類方法 → 本類 __call → 父類 __call。

3. 物件生命週期:__construct / __destruct

class LifeCycle {
    public function __construct($name) {
        $this->name = $name;
        echo "[$this->name] 建構完成\n";
    }
    
    public function __destruct() {
        echo "[$this->name] 解構中...\n";
        // 釋放資源、關閉連線、寫入日誌等
    }
}

$obj = new LifeCycle("A");  // 輸出: [A] 建構完成
unset($obj);                // 輸出: [A] 解構中...
// 全域變數在腳本結束時觸發 __destruct

觸發時機: 函數內區域變數在 return 時;全域變數在腳本結束時;unset() 或賦值 null 立即觸發。

4. 物件複製控制:__clone

class CloneDemo {
    public $id;
    private $resource;
    
    public function __construct() {
        $this->resource = fopen("log.txt", "w");
    }
    
    public function __clone() {
        // 複製時重新開啟資源,避免共用同一檔案控制代碼
        $this->resource = fopen("log.txt", "w");
        $this->id = uniqid(); // 重新生成唯一 ID
    }
}

$obj1 = new CloneDemo();
$obj2 = clone $obj1; // 觸發 __clone

5. 字串轉換:__toString

class ToStringDemo {
    private $value;
    
    public function __construct($v) { $this->value = $v; }
    
    public function __toString() {
        return "ToStringDemo: {$this->value}";
    }
}

$obj = new ToStringDemo(123);
echo $obj;              // 直接輸出物件 → 觸發 __toString
echo "值: $obj";        // 字串插值 → 觸發 __toString
printf("%s", $obj);     // PHP 5.2+ 支援 %s 修飾符

限制: 無法用於非字串語境 (如 %d、陣列索引、數學運算)。

6. 序列化控制:__sleep / __wakeup

class SerializeControl {
    public $data = [];
    private $dbLink;
    
    public function __sleep() {
        // 關閉資料庫連線,只序列化 $data
        $this->dbLink = null;
        return ['data']; // 必須回傳需序列化的屬性名陣列
    }
    
    public function __wakeup() {
        // 反序列化後重建連線
        $this->dbLink = new PDO(...);
    }
}

7. 靜態匯出:__set_state (PHP 5.1+)

class StateExport {
    public $a = 1;
    public $b = 2;
    
    public static function __set_state($arr) {
        $obj = new self();
        $obj->a = $arr['a'];
        $obj->b = $arr['b'];
        return $obj;
    }
}

$obj = new StateExport();
eval('$new = ' . var_export($obj, true) . ';'); // 觸發 __set_state

8. 物件當函數呼叫:__invoke (PHP 5.3+)

class CallableObject {
    public function __invoke($x, $y) {
        return $x + $y;
    }
}

$fn = new CallableObject();
echo $fn(3, 5); // 輸出 8,物件可直接像函數一樣呼叫

9. 自動載入:__autoload (已棄用,建議用 spl_autoload_register)

// 單一自動載入函數 (不推薦)
function __autoload($class) {
    include "classes/$class.php";
}

// 現代做法:註冊多個載入器
spl_autoload_register(function($class) {
    // PSR-4 風格載入邏輯
});

class Loader {
    public static function autoload($class) { /* ... */ }
}
spl_autoload_register(['Loader', 'autoload']);

警告: __autoload 中拋出的異常無法被 catch 捕獲,會導致致命錯誤。

命名規範建議

  • 自訂函數/方法 避免雙底線開頭,防止與未來魔術方法衝突
  • 所有魔術方法 必須宣告為 public
  • __callStatic 必須同時宣告 public static
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved