在 PHP 開發中,建立健全的錯誤處理體系至關重要,主要體現在以下五個方面:
當預期會發生嚴重錯誤(如資料庫連線失敗)且無法繼續執行時,可使用 die() 或 exit() 立即終止腳本:
mysql_connect('localhost', 'root', '123456')
or die('資料庫連線失敗:' . mysql_error());
缺點:過於粗暴,無法進行資源清理、日誌記錄或優雅降級,不適合複雜應用場景。
透過 set_error_handler() 註冊回呼函數,可攔截並自定義處理非致命錯誤(E_WARNING、E_NOTICE 等)。
| 參數 | 說明 |
|---|---|
error_level | 錯誤層級(整數),對應預定義常數如 E_WARNING |
error_message | 錯誤訊息字串 |
error_file | 發生錯誤的檔案名稱(可選) |
error_line | 發生錯誤的行號(可選) |
error_context | 錯誤發生時的變數陣列(可選,含所有作用域變數及其值) |
| 值 | 常數 | 說明 | 備註 |
|---|---|---|---|
| 1 | E_ERROR | 致命執行期錯誤,腳本終止 | 不可被自定義處理器捕獲 |
| 2 | E_WARNING | 執行期警告,腳本繼續 | |
| 4 | E_PARSE | 編譯期語法解析錯誤 | |
| 8 | E_NOTICE | 執行期通知,潛在問題 | |
| 16 | E_CORE_ERROR | PHP 啟動過程致命錯誤 | PHP 4+ |
| 32 | E_CORE_WARNING | PHP 啟動過程警告 | PHP 4+ |
| 64 | E_COMPILE_ERROR | 編譯期致命錯誤 | Zend 引擎產生,PHP 4+ |
| 128 | E_COMPILE_WARNING | 編譯期警告 | Zend 引擎產生,PHP 4+ |
| 256 | E_USER_ERROR | 用戶觸發的致命錯誤 | trigger_error() 產生,PHP 4+ |
| 512 | E_USER_WARNING | 用戶觸發的警告 | trigger_error() 產生,PHP 4+ |
| 1024 | E_USER_NOTICE | 用戶觸發的通知 | trigger_error() 產生,PHP 4+ |
| 2048 | E_STRICT | 程式碼改進建議 | PHP 5+ |
| 4096 | E_RECOVERABLE_ERROR | 可捕獲的致命錯誤 | 若未捕獲轉為 E_ERROR,PHP 5.2+ |
| 8192 | E_DEPRECATED | 棄用功能警告 | PHP 5.3+ |
| 16384 | E_USER_DEPRECATED | 用戶觸發的棄用警告 | PHP 5.3+ |
| 30719 | E_ALL | 所有錯誤與警告 | PHP 5.3.x 值,含 E_STRICT |
注意:E_ERROR、E_PARSE、E_CORE_ERROR 等核心致命錯誤無法被自定義錯誤處理器捕獲,因為腳本已立即終止。
在驗證用戶輸入、業務邏輯檢查等場景,可主動觸發錯誤:
// 觸發用戶級警告,不中斷腳本
trigger_error("用戶輸入格式無效", E_USER_WARNING);
// 觸發用戶級致命錯誤,中斷腳本
trigger_error("關鍵配置檔案遺失", E_USER_ERROR);
預設情況下,PHP 根據 php.ini 的 error_log 設定將錯誤寫入伺服器日誌或檔案。可使用 error_log() 發送至自訂目標:
// 發送錯誤郵件給管理員
error_log("資料庫連線失敗:$err_msg", 1, "admin@example.com", "From: system@example.com");
// 寫入自訂日誌檔案
error_log("[$timestamp] $message\n", 3, "/var/log/myapp_errors.log");
更多錯誤處理函數請參考:PHP 官方手冊 - 錯誤處理函數
PHP 5 引入物件導向的異常機制,提供比傳統錯誤處理更強大、結構化的控制流。
try {
// 可能拋出異常的程式碼
$result = riskyOperation($input);
} catch (SpecificException $e) {
// 處理特定異常類型
logError($e->getMessage());
showUserFriendlyMessage();
} catch (Exception $e) {
// 捕獲所有其他異常
logError($e->getMessage());
throw new AppException("系統暫時不可用,請稍後再試", 0, $e); // 重新拋出包裝異常
} finally {
// PHP 5.5+ 支援,無論是否發生異常都會執行
cleanupResources();
}
throw 至少需對應一個 catch。try {
$pdo = new PDO($dsn, $user, $pass);
} catch (PDOException $e) {
// 記錄詳細技術資訊供開發者除錯
error_log("DB Connection Failed: " . $e->getMessage() . "\nTrace: " . $e->getTraceAsString());
// 向用戶拋出友好、無敏感資訊的異常
throw new ServiceUnavailableException("服務暫時繁忙,請稍後重試", 503, $e);
}
try 區塊內。try 或 throw 至少對應一個 catch。catch 捕獲不同類型異常(由具體到一般排序)。catch 區塊中可再次拋出異常(throw $e; 或包裝新異常)。set_exception_handler(),將觸發致命錯誤:Uncaught Exception。abstract class AppException extends Exception {
protected $userMessage = "系統發生錯誤,請稍後再試";
public function getUserMessage() {
return $this->userMessage;
}
}
class ValidationException extends AppException {
protected $userMessage = "輸入資料驗證失敗";
}
class ServiceUnavailableException extends AppException {
protected $userMessage = "服務暫時不可用,請稍後重試";
}
這樣可在捕獲時區分技術日誌與用戶提示,實現關注點分離。