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

PHP 重載機制詳解:__get、__set、__isset、__unset、__call、__callStatic

     編輯:PHP 相關知識

前言

PHP 所謂的「重載」與 Java/C++ 不同,並非指方法簽名多態,而是指攔截不可訪問屬性/方法的魔術方法。通過 __get、__set、__call 等,可實現動態屬性、代理模式、只讀屬性、方法轉發等高級模式。本文全面解析 6 個重載魔術方法的觸發條件、參數、返回值與實戰應用。

屬性重載魔術方法

方法觸發場景參數返回值
__get($name)讀取不可訪問屬性(不存在、protected/private)$name: 屬性名屬性值(任意類型)
__set($name, $value)寫入不可訪問屬性$name: 屬性名, $value: 值無(void)
__isset($name)對不可訪問屬性調用 isset() 或 empty()$name: 屬性名bool
__unset($name)對不可訪問屬性調用 unset()$name: 屬性名無(void)

範例:動態屬性存儲

<?php
class DynamicEntity {
    private array $data = [];
    private array $allowed = ['name', 'email', 'age'];

    public function __get(string $name) {
        if (in_array($name, $this->allowed, true)) {
            return $this->data[$name] ?? null;
        }
        throw new Exception("屬性 '$name' 不允許讀取");
    }

    public function __set(string $name, $value) {
        if (in_array($name, $this->allowed, true)) {
            $this->data[$name] = $value;
            return;
        }
        throw new Exception("屬性 '$name' 不允許寫入");
    }

    public function __isset(string $name): bool {
        return in_array($name, $this->allowed, true) && isset($this->data[$name]);
    }

    public function __unset(string $name) {
        if (in_array($name, $this->allowed, true)) {
            unset($this->data[$name]);
        }
    }
}

$user = new DynamicEntity();
$user->name = 'Alice';      // __set
echo $user->name;           // __get -> Alice
echo isset($user->email);   // __isset -> false
unset($user->name);         // __unset
?>

方法重載魔術方法

方法觸發場景參數返回值
__call($name, $arguments)調用不可訪問實例方法$name: 方法名, $arguments: 參數陣列方法返回值
__callStatic($name, $arguments)調用不可訪問靜態方法$name: 方法名, $arguments: 參數陣列方法返回值

範例:方法代理與流式介面

<?php
class QueryBuilder {
    private array $clauses = [];

    public function __call(string $name, array $arguments) {
        // 支援 where、orderBy、limit 等動態方法
        if (in_array($name, ['where', 'orderBy', 'limit', 'select'], true)) {
            $this->clauses[$name] = $arguments;
            return $this; // 流式介面
        }
        throw new BadMethodCallException("方法 $name 不存在");
    }

    public function __callStatic(string $name, array $arguments) {
        if ($name === 'table') {
            $instance = new self();
            $instance->clauses['table'] = $arguments;
            return $instance;
        }
        throw new BadMethodCallException("靜態方法 $name 不存在");
    }

    public function getSql(): string {
        // 簡化構建 SQL
        $sql = 'SELECT ' . ($this->clauses['select'][0] ?? '*');
        $sql .= ' FROM ' . ($this->clauses['table'][0] ?? 'unknown');
        if (!empty($this->clauses['where'])) {
            $sql .= ' WHERE ' . implode(' AND ', $this->clauses['where']);
        }
        if (!empty($this->clauses['orderBy'])) {
            $sql .= ' ORDER BY ' . implode(', ', $this->clauses['orderBy']);
        }
        if (!empty($this->clauses['limit'])) {
            $sql .= ' LIMIT ' . $this->clauses['limit'][0];
        }
        return $sql;
    }
}

// 使用
$sql = QueryBuilder::table('users')
    ->where('id > 10')
    ->where('status = "active"')
    ->orderBy('created_at DESC')
    ->limit(10)
    ->getSql();

echo $sql;
// SELECT * FROM users WHERE id > 10 AND status = "active" ORDER BY created_at DESC LIMIT 10
?>

實戰應用場景

  1. 只讀屬性:__set 拋出異常,__get 正常返回。
  2. 資料映射 (ORM):實體類攔截屬性讀寫,自動同步資料庫。
  3. 代理/門面模式:__call 轉發給內部服務對象。
  4. 流式介面構建器:如上例 QueryBuilder、Laravel Eloquent。
  5. 向後兼容:舊代碼調用重命名方法,__call 映射到新方法。

注意事項與最佳實踐

  • 效能:魔術方法比直接訪問慢,熱點路徑避免濫用。
  • 遞歸風險:__get 內部讀取同名屬性會無限遞歸,必須用內部儲存陣列(如 $this->data[$name])。
  • 可見性:只攔截 不可訪問 的屬性/方法。public 成員不會觸發。
  • 繼承:子類可覆蓋魔術方法,parent::__call() 可調用父類邏輯。
  • IDE 支援:使用 @property、@method 註解提示動態成員。
  • PHP 8.2+:__get、__set 可聲明返回類型與參數類型。

對比:__call vs __callStatic

<?php
class Example {
    public function __call($name, $args) {
        echo "實例方法: $name\n";
    }
    public static function __callStatic($name, $args) {
        echo "靜態方法: $name\n";
    }
}

$obj = new Example();
$obj->foo();       // 實例方法: foo
Example::bar();    // 靜態方法: bar
?>

總結

PHP 重載機制通過 6 個魔術方法提供極大靈活性,是實現動態代理、ORM、流式 API 的關鍵。理解觸發條件、避免遞歸、配合註解提升 IDE 體驗,能安全發揮其威力。記住:只在真正需要動態性時使用,優先考慮顯式介面。

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