在專案開發中,完善的錯誤處理不僅能提升代碼健壯性,更能防止敏感信息洩露被攻擊者利用。PHP 提供了多層次的錯誤控制機制:錯誤級別設置、自定義錯誤處理器、異常捕獲、錯誤觸發器等。本文系統梳理核心函數與最佳實踐,並附帶可直接運行的示例代碼。
error_reporting()控制哪些錯誤會被報告。常用級別常量與數值對應表:
| 數值 | 常量 | 描述 |
|---|---|---|
| 1 | E_ERROR | 致命運行時錯誤,腳本終止 |
| 2 | E_WARNING | 非致命運行時警告,腳本繼續 |
| 4 | E_PARSE | 編譯時解析錯誤 |
| 8 | E_NOTICE | 運行時通知(如未定義變量) |
| 16 | E_CORE_ERROR | PHP 啟動致命錯誤 |
| 32 | E_CORE_WARNING | PHP 啟動警告 |
| 64 | E_COMPILE_ERROR | 編譯時致命錯誤 |
| 128 | E_COMPILE_WARNING | 編譯時警告 |
| 256 | E_USER_ERROR | 用戶觸發的致命錯誤 |
| 512 | E_USER_WARNING | 用戶觸發的警告 |
| 1024 | E_USER_NOTICE | 用戶觸發的通知 |
| 2048 | E_STRICT | 代碼規範建議(PHP 5.4+ 已併入 E_ALL) |
| 4096 | E_RECOVERABLE_ERROR | 可捕獲的致命錯誤 |
| 8191 | E_ALL | 所有錯誤(含 E_STRICT) |
常用設置示例:
// 開發環境:顯示所有錯誤
error_reporting(E_ALL);
ini_set('display_errors', '1');
// 生產環境:記錄日志,不顯示給用戶
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED);
ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', '/var/log/php_errors.log');
注意:error_reporting(0) 會完全關閉錯誤輸出,生產環境慎用,建議改用日志記錄。
set_error_handler()允許開發者定義自己的錯誤處理邏輯,完全接管 PHP 標準錯誤流程。
/**
* 自定義錯誤處理函數
* @param int $level 錯誤級別
* @param string $message 錯誤信息
* @param string $file 錯誤文件
* @param int $line 錯誤行號
* @param array $context 當前作用域變量上下文
*/
function myErrorHandler($level, $message, $file, $line, $context) {
// 格式化輸出
$log = date('Y-m-d H:i:s') . " [$level] $message in $file:$line\n";
// 可選:寫入文件、發送郵件、推送到監控系統
error_log($log, 3, '/var/log/php_custom_errors.log');
// 開發環境可直接輸出
if (ini_get('display_errors')) {
echo "$log
";
var_dump($context);
}
// 對於致命錯誤必須手動終止腳本
if ($level & (E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR)) {
exit(1);
}
return true; // 繼續執行標準處理器(若返回 false)
}
// 註冊處理器,捕獲所有級別
set_error_handler('myErrorHandler', E_ALL);
// 測試觸發
echo $undefinedVar; // 產生 E_NOTICE
關鍵點:
die()/exit())。E_ERROR、E_PARSE、E_CORE_ERROR 等編譯/啟動階段錯誤(因註冊時腳本尚未運行)。$context 包含當前作用域所有變量,調試極其有用,但可能包含敏感數據,生產環境謹慎輸出。trigger_error()在業務邏輯中主動拋出錯誤,配合自定義處理器實現統一錯誤上報。
function checkAge($age) {
if ($age < 0) {
trigger_error("年齡不能為負數: $age", E_USER_WARNING);
return false;
}
if ($age > 150) {
trigger_error("年齡異常過大: $age", E_USER_NOTICE);
}
return true;
}
// 註冊處理器演示
set_error_handler(function($level, $msg) {
$prefix = ['E_USER_ERROR'=>'ERROR', 'E_USER_WARNING'=>'WARNING', 'E_USER_NOTICE'=>'NOTICE'];
echo $prefix[$level] ?? 'UNKNOWN';
echo ": $msg\n";
});
checkAge(-5); // WARNING: 年齡不能為負數: -5
checkAge(200); // NOTICE: 年齡異常過大: 200
try-catch 與 Throwable(PHP 7+)現代 PHP 推薦使用面向對象的異常機制,配合 set_exception_handler() 捕獲未處理異常。
// 自定義業務異常
class BusinessException extends Exception {
public function __construct($message, $code = 0, $previous = null) {
parent::__construct($message, $code, $previous);
}
}
try {
$user = findUserById(999);
if (!$user) {
throw new BusinessException('用戶不存在', 404);
}
} catch (BusinessException $e) {
// 記錄業務日志
error_log("[Business] {$e->getMessage()} Code:{$e->getCode()}");
// 返回友好響應
jsonResponse(['error' => $e->getMessage()], $e->getCode());
} catch (Throwable $e) { // PHP 7+ 捕獲 Error 與 Exception
error_log("[System] {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}");
jsonResponse(['error' => '系統內部錯誤'], 500);
}
// 全局未捕獲異常處理
set_exception_handler(function(Throwable $e) {
error_log("[Uncaught] {$e->getMessage()}");
http_response_code(500);
echo '系統繁忙,請稍後再試';
});
display_errors=On,生產環境 Off + log_errors=On。set_error_handler、set_exception_handler、register_shutdown_function(捕獲致命錯誤)。BusinessException,系統錯誤走 ErrorException 轉換。掌握 PHP 多層次錯誤處理機制,能讓你的應用在面對預期外情況時依然穩健可控,也是從「能跑通」邁向「工程級質量」的關鍵一步。