解構函數 __destruct() 是物件生命週期的最後一站,常被誤解為「釋放記憶體」。本文釐清其真實作用、適用場景、執行時機與最佳實踐,並對比其他語言差異。
__destruct() 是用戶定義的邏輯鉤子,在物件引用計數歸零(或腳本結束)前自動呼叫。class FileLogger {
private $fp;
public function __construct(string $path) {
$this->fp = fopen($path, 'a');
if (!$this->fp) throw new RuntimeException("無法開啟日誌檔案: $path");
}
public function log(string $msg) {
fwrite($this->fp, date('c') . " $msg\n");
}
public function __destruct() {
if (is_resource($this->fp)) {
fclose($this->fp); // 確保檔案句柄釋放、緩衝區刷新
}
}
}
// 使用
$logger = new FileLogger('/var/log/app.log');
$logger->log('用戶登入');
$logger->log('下單成功');
// 腳本結束或 $logger = null 時自動呼叫 __destruct() 關閉檔案
| 誤區 | 真相 |
|---|---|
| __destruct 用來釋放記憶體 | PHP 自動管理記憶體,__destruct 只做邏輯清理 |
| 一定會執行 | 致命錯誤、exit()/die()、腳本超時、進程被 kill -9 可能不執行 |
| 可在 __destruct 拋出異常 | 會導致 Fatal Error,應捕獲內部異常或記錄日誌 |
| 可訪問其他物件 | 其他物件可能已銷毀,僅安全訪問純資料屬性 |
| 循環引用不會釋放 | PHP 5.3+ 循環 GC 會處理,但 __destruct 執行順序不確定 |
需確保邏輯絕對執行(即使 exit()),請改用 register_shutdown_function():
class CriticalTask {
public function __construct() {
register_shutdown_function([$this, 'cleanup']);
}
public function cleanup() {
// 即使 exit() 也會執行
file_put_contents('/tmp/cleanup.log', 'shutdown at ' . date('c'));
}
}
__destruct() 是「資源管家」而非「記憶體清道夫」。正確使用姿勢:管理外部資源(檔案、Socket、DB 連線、鎖)、執行不可延遲的善後邏輯。理解其觸發條件、順序、異常安全與替代方案,能寫出更健壯的資源管理代碼。