在 PHP 物件導向程式設計中,良好的封裝性要求將類別屬性宣告為 private。然而,頻繁的屬性讀寫操作若都透過個別的 getter/setter 方法會顯得繁瑣。PHP 5 引入了兩個魔術方法 __get() 與 __set(),讓我們能以類似 JavaBean 的方式統一處理私有屬性的存取,且無需為每個欄位單獨撰寫存取器。
$name 為屬性名稱,需回傳對應的值。這兩個方法並非預設存在,必須在類別中手動定義(如同 __construct())。只有當屬性被封裝為 private/protected 時,直接存取才會觸發這些魔術方法;若屬性為 public,則不會被呼叫。
class Person {
private $name;
private $sex;
private $age;
public function __set($property_name, $value) {
echo "自動呼叫 __set() 設定私有屬性:$property_name
";
$this->$property_name = $value;
}
public function __get($property_name) {
echo "自動呼叫 __get() 讀取私有屬性:$property_name
";
if (isset($this->$property_name)) {
return $this->$property_name;
}
return null;
}
}
$p1 = new Person();
$p1->name = "張三"; // 觸發 __set()
$p1->sex = "男";
$p1->age = 20;
echo "姓名:" . $p1->name . "
"; // 觸發 __get()
echo "性別:" . $p1->sex . "
";
echo "年齡:" . $p1->age . "
";
在 __set() 中可加入資料驗證,確保屬性值符合業務規則:
public function __set($property_name, $value) {
switch ($property_name) {
case 'age':
if (!is_int($value) || $value < 0 || $value > 150) {
throw new InvalidArgumentException("年齡必須為 0-150 之間的整數");
}
$this->$property_name = $value;
break;
case 'name':
if (empty(trim($value))) {
throw new InvalidArgumentException("姓名不可為空");
}
$this->$property_name = trim($value);
break;
default:
$this->$property_name = $value;
}
}
自動呼叫 __set() 設定私有屬性:name 自動呼叫 __set() 設定私有屬性:sex 自動呼叫 __set() 設定私有屬性:age 自動呼叫 __get() 讀取私有屬性:name 姓名:張三 自動呼叫 __get() 讀取私有屬性:sex 性別:男 自動呼叫 __get() 讀取私有屬性:age 年齡:20
@property 註解。$this->data[$name] 等內部儲存陣列。__set(),屬性即為唯讀。class Entity {
private $data = [];
private $allowed = ['name', 'email', 'age'];
public function __set($name, $value) {
if (!in_array($name, $this->allowed)) {
throw new Exception("不允許設定屬性:$name");
}
$this->data[$name] = $value;
}
public function __get($name) {
return $this->data[$name] ?? null;
}
public function __isset($name) {
return isset($this->data[$name]);
}
public function __unset($name) {
unset($this->data[$name]);
}
}
掌握 __get()/__set() 能讓 PHP 類別兼具封裝性與靈活存取,是進階物件導向開發的關鍵技巧之一。