__construct()new Class())後自動調用的第一個方法。function ClassName()(已棄用)。__construct(),類別重命名不影響構造方法名。__destruct()unset($obj)、超出作用域)前自動調用的最後一個方法。<?php
class Database {
private $pdo;
private $queryLog = [];
public function __construct(string $dsn, string $user, string $pass) {
echo "Database 建構子:建立連線<br>";
$this->pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
}
public function query(string $sql) {
$this->queryLog[] = $sql;
return $this->pdo->query($sql)->fetchAll();
}
public function __destruct() {
echo "Database 解構子:關閉連線、寫入查詢日誌<br>";
// 寫入日誌檔案
file_put_contents('/tmp/query.log', implode("\n", $this->queryLog) . "\n", FILE_APPEND);
$this->pdo = null; // 顯式斷開(雖然腳本結束會自動)
}
}
// 使用
$db = new Database('mysql:host=localhost;dbname=test', 'root', '');
$users = $db->query('SELECT * FROM users');
// 腳本結束自動調用 __destruct
?>
<?php
class ParentClass {
public function __construct() {
echo "Parent __construct<br>";
}
public function __destruct() {
echo "Parent __destruct<br>";
}
}
class ChildClass extends ParentClass {
public function __construct() {
parent::__construct(); // 必須顯式調用
echo "Child __construct<br>";
}
public function __destruct() {
echo "Child __destruct<br>";
parent::__destruct(); // 必須顯式調用
}
}
$obj = new ChildClass();
/*
輸出順序:
Parent __construct
Child __construct
...腳本結束...
Child __destruct
Parent __destruct
*/
?>
| 陷阱 | 後果 | 解決 |
|---|---|---|
| 子類未調用 parent::__construct() | 父類初始化邏輯缺失(連線未建、屬性未初始化) | 強制規範:子類構造首行調用 |
| __destruct 拋出異常 | Fatal Error: Exception thrown without a stack frame | 內部 try-catch 吞噬或記錄 |
| 循環引用導致延遲銷毀 | 資源釋放滯後、記憶體洩漏 | PHP 5.3+ GC 自動處理;手動 unset 打破循環 |
| 依賴 __destruct 執行關鍵業務(寫入 DB) | 請求異常終止時可能不執行 | 關鍵操作顯式調用 save()/commit(),不依賴解構子 |
__serialize/__unserialize 與資源管理配合 WeakReference、Finalizer(PECL)、register_shutdown_function 實現更可控的生命週期管理。