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

PHP 魔術方法 __set、__get、__isset、__unset 深度解析:私有屬性的透明存取藝術

     編輯:PHP 相關知識

前言:為什麼需要魔術方法?

封裝性要求屬性設為 private,但頻繁撰寫 getName()/setName() 既冗長又僵化。PHP 5 提供四大魔術方法:__get、__set、__isset、__unset,實現**私有屬性的透明讀寫、存在性檢查與刪除**,讓物件操作語法如同公有屬性般自然。

四大魔術方法速查表

方法觸發時機參數回傳值典型用途
__get($name)讀取不可存取屬性(private/不存在)$name:屬性名屬性值或 null透明讀取、虛擬屬性、委派存取
__set($name, $value)寫入不可存取屬性$name, $value無(void)透明寫入、驗證、日誌、觸發事件
__isset($name)isset($obj->prop) 或 empty()$namebool私有屬性存在性檢查
__unset($name)unset($obj->prop)$name無私有屬性刪除、清理關聯資源

完整實作範例:Person 類別

<?php
class Person {
    private $name, $sex, $age;

    // 透明讀取
    public function __get($property_name) {
        echo "__get() 自動調用:讀取 $property_name\n";
        return isset($this->$property_name) ? $this->$property_name : null;
    }

    // 透明寫入(可加驗證)
    public function __set($property_name, $value) {
        echo "__set() 自動調用:設定 $property_name = $value\n";
        // 例:年齡必須為正整數
        if ($property_name === 'age' && (!is_int($value) || $value < 0)) {
            throw new InvalidArgumentException('年齡必須為非負整數');
        }
        $this->$property_name = $value;
    }

    // isset()/empty() 支援
    public function __isset($nm) {
        echo "__isset() 自動調用:檢查 $nm\n";
        return isset($this->$nm);
    }

    // unset() 支援
    public function __unset($nm) {
        echo "__unset() 自動調用:刪除 $nm\n";
        unset($this->$nm);
    }
}

// 測試
$p1 = new Person();
$p1->name = '張三';   // 觸發 __set
$p1->sex  = '男';
$p1->age  = 20;

echo "姓名:{$p1->name}\n";  // 觸發 __get
echo "性別:{$p1->sex}\n";
echo "年齡:{$p1->age}\n";

var_dump(isset($p1->name)); // 觸發 __isset → bool(true)
unset($p1->name);           // 觸發 __unset
var_dump(isset($p1->name)); // 觸發 __isset → bool(false)
echo "刪除後姓名:{$p1->name}\n"; // __get 回傳 null
?>

執行輸出

__set() 自動調用:設定 name = 張三
__set() 自動調用:設定 sex = 男
__set() 自動調用:設定 age = 20
__get() 自動調用:讀取 name
姓名:張三
__get() 自動調用:讀取 sex
性別:男
__get() 自動調用:讀取 age
年齡:20
__isset() 自動調用:檢查 name
bool(true)
__unset() 自動調用:刪除 name
__isset() 自動調用:檢查 name
bool(false)
__get() 自動調用:讀取 name
刪除後姓名:

進階應用模式

1. 虛擬屬性(計算欄位)

public function __get($name) {
    if ($name === 'full_name') return $this->first_name . ' ' . $this->last_name;
    if ($name === 'is_adult')  return $this->age >= 18;
    return isset($this->$name) ? $this->$name : null;
}

2. 資料庫委派(Active Record 前身)

public function __get($name) {
    if (!isset($this->data[$name])) {
        $this->data[$name] = $this->db->query("SELECT $name FROM users WHERE id=?", $this->id);
    }
    return $this->data[$name];
}

3. 變更通知(觀察者模式)

public function __set($name, $value) {
    $old = $this->$name ?? null;
    $this->$name = $value;
    if ($old !== $value) $this->triggerEvent('changed', [$name, $old, $value]);
}

常見誤區與最佳實踐

誤區正確做法
魔術方法必須是 public建議 public,但 protected 也可運作(僅限繼承類別觸發)
用來替代所有 getter/setter僅用於動態/虛擬屬性;固定屬性仍建議显式方法(IDE 提示、型別提示、文檔化)
__get 回傳引用PHP 不支援魔術方法回傳引用,需用 &__get()(罕用)
遞迴調用導致無限循環內部存取私有屬性用 $this->data[$name] 而非 $this->$name
效能開銷大現代 PHP (7.4+) 優化極好;熱點路徑可用显式方法並搭配魔術方法作為 fallback

相關魔術方法補充

  • __call($name, $args) / __callStatic():動態方法呼叫
  • __sleep() / __wakeup():序列化控制
  • __toString():物件轉字串
  • __invoke():物件當函數呼叫
  • __debugInfo():var_dump() 自訂輸出

結語

__set/__get/__isset/__unset 是 PHP 物件導向的「語法糖」,讓封裝與便利性並存。善用它們實現虛擬屬性、資料委派、驗證攔截、變更通知等進階模式,能大幅提升程式碼表達力。但切記:**显式優於隱式**,核心業務屬性仍以標準 getter/setter 為主,魔術方法作為補完與擴充點。

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