__set($name, $value):向不可訪問屬性賦值時觸發。__get($name):讀取不可訪問屬性時觸發。「不可訪問」包含兩種情況:
private、protected 屬性在類外部訪問。<?php
error_reporting(E_ALL);
class Stu {
private $a;
private $b = 0;
public $c;
public $d = 0;
public function __get($name) {
echo "__get($name) called\n";
return 123;
}
public function __set($name, $value) {
echo "__set($name, $value) called\n";
}
}
$s = new Stu();
var_dump($s->a); // 輸出 123,觸發 __get(private)
var_dump($s->b); // 輸出 123,觸發 __get(private)
var_dump($s->c); // 輸出 NULL,不觸發 __get(public 存在)
var_dump($s->d); // 輸出 0,不觸發 __get(public 已初始化)
var_dump($s->e); // 輸出 123,觸發 __get(不存在)
$s->a = 3; // 觸發 __set(private)
$s->c = 3; // 不觸發 __set(public 可直接寫入)
$s->f = 3; // 觸發 __set(不存在)
?>
| 操作 | 屬性狀態 | 是否觸發魔術方法 | 備註 |
|---|---|---|---|
| 讀取 | private/protected | 是 (__get) | 外部不可直接讀 |
| 讀取 | public 且已初始化 | 否 | 直接返回值 |
| 讀取 | public 未初始化 / 不存在 | 是 (__get) | 視為不可訪問 |
| 寫入 | private/protected | 是 (__set) | 外部不可直接寫 |
| 寫入 | public | 否 | 直接賦值 |
| 寫入 | 不存在 | 是 (__set) | 動態屬性攔截 |
__get 中從緩存/數據庫按需載入數據。__set 統一驗證、轉義、類型轉換。__set 拋出異常或靜默忽略。$this->data[$name]。public function __isset($name) { return isset($this->data[$name]); }
public function __unset($name) { unset($this->data[$name]); }
使 isset($obj->prop)、unset($obj->prop) 能正確作用於動態屬性。
__get 內部讀取同名屬性會再次觸發,需操作內部儲存陣列 $this->data。public 或提供 getXxx()/setXxx()。@property 註解。__sleep/__wakeup 需手動處理動態屬性。