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

PHP 魔術方法完整圖解:從建構到銷毀的物件生命週期

     編輯:PHP 綜合知識

前言

PHP 魔術方法以雙底線 `__` 開頭,在物件生命週期的關鍵節點自動被觸發,是面向物件進階開發的核心工具。本文系統整理 13 個魔術方法,附上觸發時機、典型用途與程式碼片段,助你快速掌握物件行為控制。

魔術方法速查表

方法觸發時機典型應用場景
__construct()實例化物件時(`new Class()`)初始化屬性、注入依賴、建立資源連線
__destruct()物件被銷毀或腳本結束關閉資料庫連線、釋放鎖、寫入日誌
__call($name, $args)呼叫不可存取的方法(不存在或權限不足)動態代理、方法重載、API 客戶端魔術呼叫
__callStatic($name, $args)靜態呼叫不可存取的方法靜態工廠、ActiveRecord 查詢代理
__get($name)讀取不可存取的屬性資料映射、延遲載入、虛擬屬性
__set($name, $value)寫入不可存取的屬性屬性驗證、自動轉型、觸發髒標記
__isset($name)對不可存取屬性執行 `isset()`/`empty()`配合 `__get` 實現完整屬性存取語意
__unset($name)對不可存取屬性執行 `unset()`清除快取、重置虛擬屬性
__toString()物件被當作字串使用(`echo $obj`)輸出物件摘要、JSON 序列化、除錯顯示
__invoke($args...)物件被當作函式呼叫(`$obj()`)建立可呼叫物件、策略模式、中間件
__clone()執行 `clone $obj` 時深拷貝資源、重新生成唯一 ID、斷開共享引用
__sleep()`serialize($obj)` 前返回需序列化的屬性陣列、關閉資源、清理暫存
__wakeup()`unserialize($data)` 後重建資源連線、初始化非序列化屬性
__set_state($arr)`var_export()` 導出類時自定義導出格式、單例模式還原
__debugInfo()`var_dump($obj)` 時控制除錯輸出內容、隱藏敏感資訊
註:`__autoload()` 已於 PHP 7.2 棄用、PHP 8.0 移除,請改用 `spl_autoload_register()` 實現自動載入。

核心方法深度解析

1. `__construct` vs 同名建構子

<?php
class Demo {
    public function __construct() { echo "__construct\n"; }
    public function Demo() { echo "同名建構子\n"; }
}
new Demo(); // 輸出:__construct
?>

同時存在時優先呼叫 `__construct`,舊式同名建構子不再被呼叫(PHP 7.0+ 已棄用)。

2. `__call` 實現動態方法

<?php
class Router {
    private $routes = [];
    public function __call($method, $args) {
        if (strpos($method, 'get') === 0) {
            $route = lcfirst(substr($method, 3));
            return $this->routes[$route] ?? "404: $route";
        }
        throw new BadMethodCallException("Method $method not found");
    }
}
$r = new Router();
$r->routes['user'] = 'User Controller';
echo $r->getUser(); // User Controller
echo $r->getOrder(); // 404: order
?>

3. `__get`/`__set` 實現屬性代理與驗證

<?php
class Entity {
    private $data = [];
    private $dirty = [];
    public function __get($name) {
        return $this->data[$name] ?? null;
    }
    public function __set($name, $value) {
        $this->data[$name] = $value;
        $this->dirty[$name] = true;
    }
    public function getDirty() { return $this->dirty; }
}
$e = new Entity();
$e->name = 'Tom'; // 觸發 __set
echo $e->name;    // 觸發 __get
print_r($e->getDirty()); // ['name' => true]
?>

4. `__clone` 深拷貝範例

<?php
class Profile {
    public $tags = [];
}
class User {
    public $name;
    public $profile;
    public function __construct($name) {
        $this->name = $name;
        $this->profile = new Profile();
    }
    public function __clone() {
        $this->profile = clone $this->profile; // 深拷貝關聯物件
        $this->name .= '_copy';
    }
}
$u1 = new User('Alice');
$u1->profile->tags = ['vip'];
$u2 = clone $u1;
$u2->profile->tags[] = 'admin';
print_r($u1->profile->tags); // ['vip'] 未受影響
?>

5. `__sleep`/`__wakeup` 序列化控制

<?php
class Service {
    private $pdo; // 資源無法序列化
    private $config = ['host' => 'localhost'];
    public function __sleep() {
        return ['config']; // 僅序列化 config
    }
    public function __wakeup() {
        $this->pdo = new PDO("mysql:host={$this->config['host']}"); // 重建連線
    }
}
?>

6. `__invoke` 讓物件變成可呼叫單元

<?php
class Multiplier {
    private $factor;
    public function __construct($factor) { $this->factor = $factor; }
    public function __invoke($value) { return $value * $this->factor; }
}
$double = new Multiplier(2);
echo $double(5); // 10
// 可直接作為 callback
$arr = [1,2,3];
print_r(array_map($double, $arr)); // [2,4,6]
?>

7. `__debugInfo` 客製化除錯輸出

<?php
class ApiKey {
    public $key = 'sk_live_abc123';
    public $createdAt = '2024-01-01';
    public function __debugInfo() {
        return [
            'key' => 'sk_live_****' . substr($this->key, -4),
            'createdAt' => $this->createdAt,
        ];
    }
}
var_dump(new ApiKey());
// 輸出隱藏完整金鑰,避免日誌洩露
?>

實務避坑指南

  • 效能:魔術方法透過內部哈希查找,頻繁存取熱點屬性建議改為實際宣告屬性。
  • 繼承:子類未覆寫魔術方法時會繼承父類行為;覆寫時記得在需要時呼叫 `parent::__construct()` 等。
  • 介面契約:魔術方法不屬於介面契約,無法用 `implements` 強制規範,團隊需約定編碼標準。
  • 序列化安全:`unserialize()` 會觸發 `__wakeup`,若輸入不可信資料可能導致物件注入攻擊,PHP 7.1+ 建議使用 `allowed_classes` 選項限制可還原的類。

快速參考卡片

生命週期順序:
new Class() → __construct
clone $obj → __clone
serialize() → __sleep → 儲存字串
unserialize() → 分配記憶體 → __wakeup
$obj() → __invoke
echo $obj → __toString
isset($obj->prop) → __isset
unset($obj->prop) → __unset
腳本結束/引用歸零 → __destruct
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved