面向對象三大特性之一封裝性,通過可見性修飾符控制屬性與方法的訪問權限,實現「高內聚、低耦合」。PHP 5 引入三種修飾符,PHP 8 新增 readonly、final 等輔助關鍵字。
| 修飾符 | 本類 | 子類 | 實例(外部) | 典型用途 |
|---|---|---|---|---|
public | ✅ | ✅ | ✅ | 對外接口、API 入口、配置屬性 |
protected | ✅ | ✅ | ❌ | 內部邏輯、模板方法、子類擴展點 |
private | ✅ | ❌ | ❌ | 核心算法、敏感數據、實現細節 |
<?php
class User {
public string $name = 'Guest'; // 公有:誰都能訪問
protected string $email = ''; // 受保護:本類+子類
private string $passwordHash = ''; // 私有:僅本類
public function __construct(string $name, string $email, string $password) {
$this->name = $name;
$this->email = $email;
$this->setPassword($password); // 內部調用私有方法
}
// 公有介面:註冊入口
public function register(): bool {
return $this->validate() && $this->save();
}
// 受保護:子類可覆蓋驗證邏輯
protected function validate(): bool {
return filter_var($this->email, FILTER_VALIDATE_EMAIL)
&& strlen($this->passwordHash) > 0;
}
// 私有:核心加密邏輯,禁止覆蓋、禁止外部調用
private function setPassword(string $plain): void {
$this->passwordHash = password_hash($plain, PASSWORD_BCRYPT, ['cost' => 12]);
}
// 公有:安全驗證介面
public function verifyPassword(string $plain): bool {
return password_verify($plain, $this->passwordHash);
}
// 受保護:持久化模板方法
protected function save(): bool {
// 模擬數據庫保存
return true;
}
}
class AdminUser extends User {
private array $permissions = [];
public function __construct(string $name, string $email, string $password, array $perms) {
parent::__construct($name, $email, $password);
$this->permissions = $perms;
}
// 覆蓋受保護方法:擴展驗證邏輯
protected function validate(): bool {
return parent::validate() && !empty($this->permissions);
}
// 子類新增公有介面
public function hasPermission(string $perm): bool {
return in_array($perm, $this->permissions);
}
// ❌ 錯誤示範:無法訪問父類私有屬性/方法
// public function debug() { echo $this->passwordHash; } // Fatal Error
// public function resetPwd() { $this->setPassword('123'); } // Fatal Error
}
// 實例測試
$user = new User('Alice', 'alice@example.com', 'secret123');
echo $user->name; // ✅ public
// echo $user->email; // ❌ protected
// echo $user->passwordHash; // ❌ private
$user->register(); // ✅ public
// $user->validate(); // ❌ protected
// $user->setPassword('x'); // ❌ private
$user->verifyPassword('secret123'); // ✅ public
$admin = new AdminUser('Bob', 'bob@admin.com', 'admin123', ['user:manage', 'log:view']);
$admin->hasPermission('user:manage'); // ✅ 子類公有方法
?>
public、protected 成員private 成員(包括屬性與方法)class Parent {
protected function foo() {}
}
class Child extends Parent {
public function foo() {} // ✅ protected → public 擴大
// private function foo() {} // ❌ 縮小會報錯
}
self vs parent vs $this| 關鍵字 | 含義 | 訪問範圍 | 典型場景 |
|---|---|---|---|
$this | 當前實例對象 | 實例屬性/方法(受可見性限制) | 調用對象自身方法、訪問實例屬性 |
self | 當前類(靜態上下文) | 類常量、靜態屬性、靜態方法(含 private) | self::CONST、self::$staticProp、self::staticMethod() |
parent | 父類(靜態上下文) | 父類 public/protected 靜態成員、父類構造函數 | parent::__construct()、parent::CONST、parent::staticMethod() |
static | 晚靜態綁定(調用類) | 運行時實際調用的類的靜態成員 | 靜態工廠模式、ActiveRecord 查詢 |
class A {
public static $name = 'A';
public static function who() { echo self::$name; } // 總是 A
public static function whoLate() { echo static::$name; } // 運行時類
}
class B extends A {
public static $name = 'B';
}
B::who(); // 輸出 A
B::whoLate(); // 輸出 B
class Counter {
private static int $count = 0; // 僅本類訪問
protected static string $prefix = 'CNT_'; // 子類可訪問
public static function increment(): void {
self::$count++;
}
public static function get(): string {
return self::$prefix . self::$count;
}
}
class SubCounter extends Counter {
public static function reset(): void {
// self::$count = 0; // ❌ 無法訪問父類 private static
parent::$count = 0; // ❌ parent 也無法訪問 private
// 正確做法:父類提供 protected 重置方法
}
}
readonly)class Product {
private float $price = 0;
public function getPrice(): float { return $this->price; }
public function setPrice(float $price): void {
if ($price < 0) throw new InvalidArgumentException('價格不能為負');
$this->price = $price;
}
}
readonly 修飾符
class DTO {
public function __construct(
public readonly string $id,
public readonly string $name,
public readonly DateTimeImmutable $createdAt
) {}
}
| 錯誤 | 原因 | 修正 |
|---|---|---|
| 子類嘗試覆蓋 private 方法 | private 不參與繼承,子類定義同名方法是新方法 | 改為 protected 或重新設計 |
| 靜態上下文用 $this | $this 僅在實例方法中可用 | 改用 self:: 或 static:: |
| parent:: 訪問私有靜態成員 | parent 同受可見性限制 | 父類提供 protected 存取器 |
| 接口中聲明 private 方法 | 接口只能定義 public 契約 | 移除修飾符或改用 abstract class |
$user = new User('Test', 'test@test.com', 'pwd');
$ref = new ReflectionClass($user);
// 訪問私有屬性
$prop = $ref->getProperty('passwordHash');
$prop->setAccessible(true);
echo $prop->getValue($user); // 獲取私有屬性值
// 調用私有方法
$method = $ref->getMethod('setPassword');
$method->setAccessible(true);
$method->invoke($user, 'newpass');
警告:業務代碼嚴禁使用 Reflection 破壞封裝,僅用於單元測試、序列化庫、ORM 框架、依賴注入容器等基礎設施層。
public、protected、private 構成 PHP 封裝性的基石。核心原則:實現細節私有化、擴展點保護化、對外契約公有化。配合 readonly、final、static 晚綁定、介面隔離,即可設計出高內聚、低耦合、易維護、可擴展的類體系。記住:可見性是給開發者看的約束,不是給機器看的限制——良好的設計讓正確的用法最簡單,錯誤的用法最困難。