構造函數與析構函數是 PHP 面向對象編程中對象生命週期的兩個關鍵節點。掌握它們的調用時機、繼承規則、異常處理與資源管理模式,是寫出健壯類庫的基礎。
$obj = new ClassName($args);。__construct()。PHP 不支持方法重載,構造函數通過可選參數 + 預設值或靜態工廠方法實現多態構造:
<?php
class User {
private $id, $name, $email;
// 主構造:完整數據
public function __construct(int $id, string $name, string $email) {
$this->id = $id; $this->name = $name; $this->email = $email;
}
// 工廠方法:從數組創建
public static function fromArray(array $data): self {
return new self($data['id'], $data['name'], $data['email']);
}
// 工廠方法:從數據庫行創建
public static function fromRow(PDOStatement $stmt): self {
$row = $stmt->fetch(PDO::FETCH_ASSOC);
return self::fromArray($row);
}
}
// 使用
$u1 = new User(1, 'Tom', 'tom@example.com');
$u2 = User::fromArray(['id'=>2, 'name'=>'Jerry', 'email'=>'jerry@example.com']);
__construct → 自動調用父類 __construct(若存在)。__construct → 必須显式調用 parent::__construct(...),否則父類初始化邏輯缺失。unset($obj)、$obj = null。<?php
class DbConnection {
private $pdo;
public function __construct($dsn, $user, $pass) {
$this->pdo = new PDO($dsn, $user, $pass);
}
public function query($sql) { return $this->pdo->query($sql); }
public function __destruct() {
// 關閉連接、釋放句柄、寫入審計日誌
$this->pdo = null; // 觸發 PDO 內部關閉
error_log("DB connection closed for " . spl_object_id($this));
}
}
| 陷阱 | 說明 | 對策 |
|---|---|---|
| 析構函數中拋出異常 | PHP 會忽略並觸發 Fatal Error (Exception thrown without a stack frame) | 析構函數內捕獲所有異常,僅記錄日誌 |
| 依賴析構函數釋放關鍵資源 | 腳本異常退出、致命錯誤、內存耗盡時可能不調用 | 關鍵資源用 try/finally 或 register_shutdown_function 保證釋放 |
| 循環引用導致延遲回收 | $a->b = $b; $b->a = $a; 引用計數不歸零,需等 gc_collect_cycles() | 避免循環引用,或手動 unset 打破環,關鍵資源用弱引用 |
| 析構順序不確定 | 全局對象、靜態變量析構順序不可控 | 不在析構函數中依賴其他全局對象/服務 |
__construct() // 實例化後
__destruct() // 銷毀前
__call($name,$args) // 調用不可訪問方法
__callStatic($name,$args) // 靜態調用不可訪問方法
__get($name) // 讀取不可訪問屬性
__set($name,$value) // 寫入不可訪問屬性
__isset($name) // isset() 不可訪問屬性
__unset($name) // unset() 不可訪問屬性
__sleep() // serialize() 前
__wakeup() // unserialize() 後
__serialize() / __unserialize() // PHP 7.4+ 自定義序列化
__toString() // echo $obj / (string)$obj
__invoke($args) // $obj() 當函數調用
__set_state($arr) // var_export() 導出類
__clone() // clone $obj 後
__debugInfo() // var_dump() 顯示屬性控制
<?php
class Lock {
private $fp;
public function __construct(string $lockFile) {
$this->fp = fopen($lockFile, 'c+');
if (!flock($this->fp, LOCK_EX | LOCK_NB)) {
throw new RuntimeException("Lock failed");
}
}
public function __destruct() {
flock($this->fp, LOCK_UN);
fclose($this->fp);
}
}
// 使用:作用域結束自動釋放鎖
function criticalSection() {
$lock = new Lock('/tmp/my.lock');
// 業務邏輯...
// 函數返回/異常拋出 → $lock 銷毀 → __destruct 釋放鎖
}
__construct:初始化狀態、驗證參數、建立連接、註冊觀察者。__destruct:釋放非內存資源 (文件句柄、Socket、鎖、臨時文件)、記錄審計日誌。try/finally、上下文管理器模式保證確定性釋放。掌握構造/析構的生命週期契約,是 PHP 面向對象從「會用語法」進階到「設計健壯系統」的分水嶺。