錯誤報告是 PHP 開發調試的「儀表盤」。正確理解錯誤層級、靈活配置報告策略、建立生產級日誌體系,能讓你從「看報錯」進化到「預防錯、快速定位錯、優雅處理錯」。
| 常量 | 數值 | 嚴重度 | 行為 | 典型場景 |
|---|---|---|---|---|
E_DEPRECATED | 8192 | 最低 | 運行時提示,腳本繼續 | 使用已棄用函數(如 each())、動態屬性(PHP 8.2+) |
E_USER_DEPRECATED | 16384 | 最低 | 用戶觸發,腳本繼續 | 自定義庫標記棄用 API |
E_NOTICE | 8 | 低 | 運行時通知,腳本繼續 | 訪問未定義變量/數組鍵、靜態調用非靜態方法 |
E_USER_NOTICE | 1024 | 低 | 用戶觸發通知 | 業務邏輯提示(如緩存未命中) |
E_WARNING | 2 | 中 | 運行時警告,腳本繼續 | 文件不存在、參數類型錯誤、除零警告、資源操作失敗 |
E_USER_WARNING | 512 | 中 | 用戶觸發警告 | 業務規則違規但非致命(如庫存不足降級處理) |
E_RECOVERABLE_ERROR | 4096 | 中高 | 可捕獲致命錯誤 | 類型聲明不匹配(PHP 7+),可用 set_error_handler 捕獲轉為異常 |
E_ERROR | 1 | 高 | 致命錯誤,腳本終止 | 調用未定義函數、實例化不存在類、內存耗盡、require 失敗 |
E_USER_ERROR | 256 | 高 | 用戶觸發致命錯誤 | 關鍵業務斷言失敗(如配置缺失) |
E_PARSE | 4 | 最高 | 編譯時語法解析錯誤 | 語法錯誤(少分號、括號不匹配),腳本根本不執行 |
E_CORE_ERROR | 16 | 最高 | PHP 啟動過程核心錯誤 | 擴展加載失敗 |
E_COMPILE_ERROR | 64 | 最高 | 編譯過程核心錯誤 | require_once 失敗、declare 錯誤 |
註: E_ALL (32767) 包含除 E_STRICT 外所有級別(PHP 5.4+ E_STRICT 已併入 E_ALL)。
; 錯誤報告級別位掩碼
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT ; 開發建議
error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED ; 生產常見
; 是否在輸出中顯示錯誤
display_errors = On ; 開發環境
display_errors = Off ; 生產環境(嚴禁開啟,防信息洩露)
; 是否記錄錯誤日誌
log_errors = On
error_log = /var/log/php/error.log ; 絕對路徑,確保目錄可寫
; 日誌最大長度(0 不限制)
log_errors_max_len = 1024
; 忽略重複錯誤
ignore_repeated_errors = Off
ignore_repeated_source = Off
<?php
// 僅影響當前腳本及後續 include 文件
error_reporting(E_ALL); // 開發:報告所有
error_reporting(E_ALL & ~E_NOTICE); // 忽略 Notice
error_reporting(0); // 關閉報告(慎用)
// 獨立控制顯示與記錄
ini_set('display_errors', '1'); // 瀏覽器顯示
ini_set('log_errors', '1'); // 寫入日誌
ini_set('error_log', '/tmp/my.log'); // 指定日誌文件
// 組合策略:開發全顯、生產只記不顯
if (APP_ENV === 'dev') {
error_reporting(-1); // -1 等同 E_ALL,包含未來新增級別
ini_set('display_errors', '1');
} else {
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', LOG_DIR . '/php_error.log');
}
?>
利用 set_error_handler() 將非致命錯誤轉為 ErrorException,統一進入 try-catch 流程:
<?php
function exceptionErrorHandler($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) return false; // 當前級別未開啟報告
throw new ErrorException($message, 0, $severity, $file, $line);
}
set_error_handler('exceptionErrorHandler');
// 現在可捕獲 Warning/Notice
try {
$arr = [];
echo $arr['missing_key']; // 原產生 Notice,現拋出 ErrorException
} catch (ErrorException $e) {
Log::warning('捕獲轉換異常', ['msg' => $e->getMessage(), 'file' => $e->getFile()]);
}
// 致命錯誤需註冊關閉函數捕獲
register_shutdown_function(function() {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE])) {
Log::emergency('致命錯誤', $error);
// 發送告警、渲染友好錯誤頁
}
});
<?php
// 業務邏輯中主動上報
if ($stock < $buyNum) {
trigger_error("商品 {$goodsId} 庫存不足: {$stock} < {$buyNum}", E_USER_WARNING);
return false; // 繼續執行降級邏輯
}
if (empty($config['app_key'])) {
trigger_error('核心配置 app_key 缺失', E_USER_ERROR); // 腳本終止
}
timestamp, level, message, context(trace_id, user_id, request_id, code_stack),便於 ELK/Splunk 接入分析。error.log (ERROR+), warning.log (WARNING), info.log (NOTICE/INFO),避免單文件過大。logrotate 每日切割、壓縮、保留 30 天,防止磁盤爆滿。***)。TypeError, ArgumentCountError 等 Error 子類,**不再觸發 E_RECOVERABLE_ERROR**,需 catch (Throwable $e) 捕獲(Exception 捕獲不到)。E_ALL,無需單獨處理。E_DEPRECATED,建議聲明屬性或用 #[\AllowDynamicProperties]。E_ERROR。| 場景 | error_reporting | display_errors | log_errors | 備註 |
|---|---|---|---|---|
| 本地開發 | -1 (E_ALL) | On | On | 全量可見,即時調試 |
| 測試/預發布 | E_ALL & ~E_DEPRECATED | Off | On | 不干擾 UI,日誌全留存 |
| 生產環境 | E_ALL & ~E_NOTICE & ~E_DEPRECATED | Off | On | 屏蔽雜訊,核心錯誤必記 |
| CLI 批量任務 | E_ALL | Off | On | 配合 nohup/supervisor,日誌唯一真相 |
錯誤級別不是枯燥的常量表,而是「可觀測性」的分層開關。掌握:
set_error_handler + register_shutdown_function 將錯誤納入異常體系,單一處理入口。把錯誤當作「系統在對你說話」,配置得當,它就是最忠實的質量衛士。