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

PHP 5 重載機制詳解:__call、__set、__get 動態代理與屬性攔截實戰

     編輯:PHP 相關知識

PHP 5 重載機制詳解:__call、__set、__get 動態代理與屬性攔截實戰

本文為《Core PHP Programming》筆記第十一節,深入講解 PHP 5 面向對象重載(Overloading)機制:__call() 攔截不可訪問方法調用、__set()/__get() 攔截不可訪問屬性讀寫、動態屬性存儲、參數類型分發實現偽重載、與 Java/C++ 重載的本質區別。

1. 重載觸發條件

  • 僅在成員不可訪問時觸發:不存在、private/protected 且調用處無權限。
  • public 成員直接訪問,不經魔術方法。
  • 這與 Java/C++ 編譯時多態(同名不同參數)本質不同,屬於運行時動態代理。

2. 魔術方法簽名

class Overloader {
    private $data = [];

    // 屬性寫入攔截
    public function __set(string $name, $value): void { $this->data[$name] = $value; }
    // 屬性讀取攔截
    public function __get(string $name) { return $this->data[$name] ?? null; }
    // 方法調用攔截
    public function __call(string $name, array $args) {
        echo "Invoking $name() with args: "; print_r($args);
        return true;
    }
}
$o = new Overloader();
$o->dynaProp = "Dynamic"; // 觸發 __set
echo $o->dynaProp;        // 觸發 __get
$o->dynaMethod("Leon", "Zeev"); // 觸發 __call

3. 實戰模式:動態屬性存儲(類數組訪問)

class DynamicEntity {
    private $attributes = [];
    public function __set($name, $value) { $this->attributes[$name] = $value; }
    public function __get($name) { return $this->attributes[$name] ?? null; }
    public function __isset($name) { return isset($this->attributes[$name]); }
    public function __unset($name) { unset($this->attributes[$name]); }
    public function toArray() { return $this->attributes; }
}
$e = new DynamicEntity();
$e->title = "Hello";
$e->views = 123;
echo $e->title; // Hello
var_dump($e->toArray());
  • 適用於 ORM 模型、配置對象、API 響應包裝等結構不固定場景。
  • 配合 __isset/__unset 完整支持 isset()/unset() 語法。

4. 實戰模式:__call 實現參數類型分發(偽重載)

class Math {
    public function __call($name, $args) {
        if ($name === 'add') {
            if (count($args) === 2 && is_int($args[0]) && is_int($args[1])) {
                return $this->addInt($args[0], $args[1]);
            }
            if (count($args) === 2 && is_float($args[0]) && is_float($args[1])) {
                return $this->addFloat($args[0], $args[1]);
            }
            if (count($args) === 3 && is_array($args[0])) {
                return $this->addArray($args[0]);
            }
        }
        throw new BadMethodCallException("Method $name not supported with these args");
    }
    private function addInt($a, $b) { return $a + $b; }
    private function addFloat($a, $b) { return $a + $b; }
    private function addArray(array $arr) { return array_sum($arr); }
}
$m = new Math();
echo $m->add(1, 2);       // 3 (int)
echo $m->add(1.5, 2.5);   // 4.0 (float)
echo $m->add([1,2,3]);    // 6 (array)
  • 運行時根據參數數量/類型分發到私有專用方法。
  • 缺點:IDE 無法靜態分析、文檔生成困難、性能略低於顯式多方法。
  • 現代 PHP (7.0+) 建議使用聯合類型 / 重載方法簽名(PHP 8.0+ 不支持重載,但可用聯合類型 + 內部分發)替代。

5. 訪問控制對重載的影響

class Test {
    private $secret = 42;
    public function __get($name) {
        if ($name === 'secret') return '***'; // 過濾私有屬性外部讀取
        return null;
    }
}
$t = new Test();
echo $t->secret; // *** (觸發 __get,因 private 不可外部直接讀)
echo $t->publicProp; // null (不存在也觸發 __get)

6. 性能與最佳實踐

  • 魔術方法調用比直接調用慢 ~2-5 倍(微秒級),高頻熱路徑避免使用。
  • 始終實現 __isset/__unset 保持語言語義一致性。
  • 拋出 BadMethodCallException 而非靜默返回 null,便於調試。

7. PHP 4 vs PHP 5 重載

  • PHP 4 重載(overload())僅用於外部對象映射(COM/Java),語法異常複雜。
  • PHP 5 原生魔術方法重載語法清晰、功能強大,成為動態特性基石。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
場景建議
固定結構實體顯式聲明屬性 + getter/setter,避免魔術方法開銷
動態/擴展屬性使用 __set/__get + 內部數組存儲
API 代理/遠程調用__call 轉發至 RPC 客戶端
框架核心/基類謹慎使用,文檔化所有動態行為