將屬性設為 private,提供 public 方法(Getter/Setter)或魔術方法進行驗證、轉換、日誌、延遲載入等邏輯。
| 修飾符 | 類內部 | 子類 | 外部 | 用途 |
|---|---|---|---|---|
| public | ✓ | ✓ | ✓ | 公開 API |
| protected | ✓ | ✓ | ✗ | 繼承體系共享、內部協作 |
| private | ✓ | ✗ | ✗ | 核心狀態、不變量、強封裝 |
<?php
class Person {
private $name;
private $age;
private $sex;
public function __construct($name, $age, $sex) {
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
// 讀取不可見/不存在屬性時觸發
public function __get($name) {
if ($name === 'age') {
// 業務規則:40 歲以上顯示減 10 歲
return $this->age > 40 ? $this->age - 10 : $this->age;
}
if (property_exists($this, $name)) {
return $this->$name;
}
throw new Exception("Property '$name' not accessible");
}
// 寫入不可見/不存在屬性時觸發
public function __set($name, $value) {
if ($name === 'age') {
// 驗證:年齡 0-100
if ($value < 0 || $value > 100) {
throw new InvalidArgumentException("Age must be 0-100");
}
$this->age = $value;
return;
}
if (property_exists($this, $name)) {
$this->$name = $value;
return;
}
throw new Exception("Property '$name' not writable");
}
// isset() 檢測不可見屬性時觸發
public function __isset($name) {
return property_exists($this, $name) && $this->$name !== null;
}
// unset() 刪除不可見屬性時觸發
public function __unset($name) {
if (property_exists($this, $name)) {
$this->$name = null;
}
}
// 公開方法替代直接存取(推薦)
public function setAge($age) {
if ($age < 0 || $age > 100) return false;
$this->age = $age;
return true;
}
public function getAge() {
return $this->age > 40 ? $this->age - 10 : $this->age;
}
public function say() {
echo "我是 {$this->name},{$this->getAge()} 歲,{$this->sex}<br>";
$this->run(); // 內部調用私有方法
}
private function run() {
echo "跑步中...<br>";
}
}
$p = new Person("張三", 45, "男");
echo $p->age; // 觸發 __get -> 輸出 35 (45-10)
$p->age = 50; // 觸發 __set -> 成功
$p->age = 150; // 觸發 __set -> 拋出異常
isset($p->name); // 觸發 __isset -> true
unset($p->name); // 觸發 __unset -> name 變 null
$p->say(); // 正常調用
// $p->run(); // 錯誤:私有方法不可見
?>
readonly 屬性(PHP 8.1+)。readonly,無業務邏輯,純數據載體。<?php
class UserDTO {
public function __construct(
public readonly int $id,
public readonly string $email,
public readonly string $name
) {}
}
// 建構後不可修改,無需 Getter/Setter,完美封裝不可變數據
?>