PHP 物件導向中,__construct() 與 __destruct() 在繼承體系中的呼叫順序與規則,常被誤解導致資源洩漏或重複初始化。本文透過圖文並茂的程式碼範例,系統說明子類別有無自訂建構子/解構子時的行為差異、parent::__construct() 手動鏈結最佳實踐、覆寫規則(存取權限、參數相容性)、final 關鍵字限制繼承與覆寫。
| 情境 | 行為 | 程式碼示意 |
|---|---|---|
子類別未定義 __construct() | 自動呼叫父類別建構子 | class Child extends Parent {} → new Child($arg) 需符合父類別簽名 |
子類別定義 __construct() | 不會自動呼叫父類別,需手動 parent::__construct(...) | class Child extends Parent { function __construct($a) { parent::__construct($a); } } |
| 子類別建構子參數與父類別不同 | 允許(建構子覆寫參數簽名不受 LSP 限制) | 父 __construct($x),子 __construct($x, $y = 1) 合法 |
__destruct() → 自動呼叫父類別解構子__destruct() → 不會自動呼叫父類別,需手動 parent::__destruct()class Base {
protected $conn;
public function __construct($dsn) {
$this->conn = new PDO($dsn);
echo "Base: 連線建立\n";
}
public function __destruct() {
$this->conn = null;
echo "Base: 連線關閉\n";
}
}
class Child extends Base {
private $cache;
public function __construct($dsn, $cacheDir) {
parent::__construct($dsn); // 1. 先完成父類別初始化
$this->cache = new FilesystemCache($cacheDir); // 2. 再初始化自身資源
echo "Child: 快取初始化\n";
}
public function __destruct() {
echo "Child: 快取清理\n";
parent::__destruct(); // 3. 最後釋放父類別資源
}
}
| 規則 | 說明 | 違反後果 |
|---|---|---|
| 存取權限不得降低 | 父 public → 子只能 public;父 protected → 子 protected/public;父 private → 子不可覆寫(視為全新方法) | Fatal error: Access level... |
| 參數簽名應保持相容 | 參數名可不同,但型別、預設值、必選/可選順序應相容(LSP) | 執行時型別錯誤、靜態分析警告 |
| 回傳型別共變 | PHP 7.4+ 支援回傳型別共變(子類別回傳更具體型別) | 型別錯誤 |
final class SecurityCore { } // 禁止被繼承
class Auth {
final public function verifyToken($token) { } // 禁止被覆寫
}
適用場景:核心安全邏輯、單例模式、不可變值物件、框架核心基類。
private 屬性/方法:子類別不可存取、不可覆寫,但可在子類別中定義同名私有成員(視為獨立新成員)。parent::__construct(...)。try-catch 吞噬例外或記錄日誌。unset() 或實作 __destruct() 斷開循環。掌握 PHP 建構子/解構子在繼承中的「自動與手動」呼叫機制,遵循「先父後子、後子先父」的資源管理原則,善用 final 保護核心邏輯,即能編寫健壯、可維護的物件導向程式碼。建議在 Code Review 中將「建構子是否正確鏈結父類別」列為必檢項目。