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

PHP 類別攔截器(魔術方法)深度用法指南

     編輯:PHP 綜合知識

PHP 的魔術方法(常稱攔截器)讓物件能在執行期動態響應未定義的屬性與方法操作。本文針對 __get、__set、__call、__isset、__unset 五大核心攔截器,提供精簡範例與實務解析,幫助開發者快速掌握其行為差異與適用場景。

1. __get($property) — 讀取不存在屬性

當嘗試讀取未宣告或不可見屬性時自動觸發。

class Lanjie {
    public function __get($name) {
        echo "$name property not found!";
    }
}
$ob = new Lanjie();
echo $ob->g; // 輸出:g property not found!

典型用途:動態屬性代理、延遲載入、唯讀屬性保護。

2. __set($property, $value) — 寫入不存在屬性

搭配 method_exists 實現「設值自動轉發給 setXxx()」模式:

class Person {
    private $_age;
    private $_name;

    public function __set($name, $value) {
        $method = 'set' . ucfirst($name);
        if (method_exists($this, $method)) {
            return $this->$method($value);
        }
    }

    public function setName($name) {
        $this->_name = $name;
        if (!is_null($this->_name)) {
            $this->_name = strtoupper($this->_name); // 自動大寫
        }
    }
    public function setAge($age) { $this->_age = (int)$age; }
}

$p = new Person();
$p->name = 'bob'; // 觸發 __set -> setName('bob') -> 儲存 'BOB'
print_r($p);      // _name => 'BOB'

關鍵點:可在 setter 中加入驗證、轉換、事件觸發等橫切邏輯。

3. __call($method, $args) — 呼叫不存在方法

最常用、彈性最大的攔截器,常見於:

  • 動態 API 代理(如 $api->getUserList() → HTTP GET)
  • 流式介面建構器
  • 尋找 getter/setter 替代品
class ApiClient {
    public function __call($method, $args) {
        // 簡易 REST 映射:getXxx -> GET /xxx
        if (preg_match('/^get(.+)/', $method, $m)) {
            $endpoint = strtolower($m[1]);
            return $this->request('GET', "/$endpoint", $args[0] ?? []);
        }
        throw new BadMethodCallException("Method $method not found");
    }
    private function request($verb, $uri, $params) { /* ... */ }
}

4. __isset($property) & __unset($property)

分別攔截 isset($obj->prop) / empty($obj->prop) 與 unset($obj->prop):

class Entity {
    private $data = [];
    public function __isset($k) { return array_key_exists($k, $this->data); }
    public function __unset($k) { unset($this->data[$k]); }
    public function __set($k, $v) { $this->data[$k] = $v; }
    public function __get($k) { return $this->data[$k] ?? null; }
}
$e = new Entity();
$e->foo = 'bar';
var_dump(isset($e->foo)); // true
unset($e->foo);
var_dump(isset($e->foo)); // false

實務比較與選擇建議

場景推薦方案備註
固定結構實體明確宣告屬性 + getter/setterIDE 支援、型別安全、效能最佳
動態屬性包(DTO、配置)陣列儲存 + __get/__set/__isset/__unset實作 ArrayAccess 更標準
方法級代理(RPC、Builder)__call注意回傳 $this 實現鏈式呼叫
只讀/計算屬性__get + 私有快取避免重複計算

常見坑與避雷指南

  1. 遞迴炸裂:__get 內部讀取 $this->$name 會再次觸發 __get → 必須操作私有儲存陣列或不同名屬性。
  2. 靜態分析失效:PHPStan/Psalm 無法推導動態屬性型別,建議加上 @property、@method 註解或改用陣列存取。
  3. 序列化遺漏:__sleep 需手動列出動態屬性鍵名,否則 serialize() 會遺失。
  4. 效能陷阱:熱路徑(如迴圈內頻繁存取)請改用陣列或定義實際屬性,魔術方法有函數呼叫開銷。
  5. 繼承衝突:父類已定義 __get,子類覆寫時別忘 parent::__get() 或完整接管邏輯。

總結

攔截器是 PHP 元編程的核心工具,__call 應用最廣,__get/__set 適合動態屬性封裝,__isset/__unset 補足語義完整性。實務上遵循「核心領域模型显式化,邊界適配層隱式化」原則,配合註解輔助靜態分析,即可兼得靈活與可維護性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved