妥善控制 PHP 錯誤輸出,既能在開發期快速定位問題,又能在生產環境避免敏感資訊洩露。本文系統整理程式碼層、配置層、日誌層三大控制維度,並針對 display_errors = Off 失效的常見成因提供解決方案。
| 常數 | 值 | 說明 |
|---|---|---|
| E_ERROR | 1 | 嚴重執行錯誤,腳本終止 |
| E_WARNING | 2 | 執行警告,腳本繼續 |
| E_PARSE | 4 | 編譯時語法解析錯誤 |
| E_NOTICE | 8 | 運行期提醒(如未定義變數) |
| E_STRICT | 2048 | 程式碼標準化建議(PHP 5.4+ 併入 E_ALL) |
| E_DEPRECATED | 8192 | 棄用功能警告(PHP 5.3+) |
| E_ALL | 32767 | 所有錯誤與警告(PHP 5.4+ 含 E_STRICT) |
<?php
// 開發環境:顯示所有錯誤(建議放在公共引入檔最頂部)
error_reporting(E_ALL);
ini_set('display_errors', '1');
// 生產環境:記錄錯誤但不顯示給用戶
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT);
ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', '/var/log/php/error.log');
// 僅報告致命錯誤
error_reporting(E_ERROR | E_WARNING | E_PARSE);
// 完全關閉錯誤報告(極不推薦,除非極特殊場景)
error_reporting(0);
?>
E_ALL ^ E_NOTICE:排除 NOTICE(異或運算,若 NOTICE 不在 E_ALL 中會反而開啟,不推薦)E_ALL & ~E_NOTICE:推薦,按位取反後 AND,穩健排除特定層級E_ALL & ~(E_NOTICE | E_DEPRECATED):同時排除多個層級| 指令 | 推薦值(開發) | 推薦值(生產) | 說明 |
|---|---|---|---|
| error_reporting | E_ALL | E_ALL & ~E_DEPRECATED & ~E_STRICT | 基礎報告層級 |
| display_errors | On | Off | 是否在輸出中顯示錯誤 |
| display_startup_errors | On | Off | PHP 啟動階段錯誤 |
| log_errors | On | On | 啟用錯誤日誌記錄 |
| error_log | /path/to/dev.log | /var/log/php/error.log | 錯誤日誌絕對路徑 |
| error_log_mode | - | 0644 | 日誌檔案權限(PHP 7.4+) |
| ignore_repeated_errors | Off | On | 忽略重複錯誤訊息 |
| track_errors | Off | Off | 將最後一個錯誤存入 $php_errormsg(已棄用) |
php --ini 或 phpinfo() 確認 Loaded Configuration File 路徑php_value display_errors 1 會蓋過 php.ini現象:php.ini 明明設定 display_errors = Off,網頁仍輸出錯誤堆疊。
| 可能原因 | 驗證方法 | 解決措施 |
|---|---|---|
| 錯誤類型為「致命錯誤」(E_ERROR/E_PARSE) | 錯誤訊息含 "Fatal error" / "Parse error" | 這類錯誤無法被 display_errors 抑制(引擎崩潰前必須輸出)。解決:修正程式碼語法、檢查擴充套件相容性 |
| 啟用了 log_errors 但未設定 error_log 路徑/權限 | phpinfo() 顯示 error_log 為空或無寫入權限 | 設定絕對路徑:error_log = "/var/log/php/error.log",確保目錄可寫(www-data/user 權限) |
| 使用 CGI/FastCGI 且 php.ini 非預期生效 | phpinfo() 顯示載入路徑非預期 | 檢查 PHPRC 環境變數、.user.ini、啟動參數 -c |
| 框架/庫自定義錯誤處理器 | 程式碼含 set_error_handler()、set_exception_handler() |
檢查處理器內部是否強制 echo / var_dump;框架通常有獨立 debug 開關(如 Laravel APP_DEBUG) |
| Xdebug 覆蓋顯示行為 | phpinfo() 有 Xdebug 區段 | 設定 xdebug.mode=off 或 xdebug.mode=develop 時調整 xdebug.var_display_max_depth 等 |
; php.ini 生產環境建議區段
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = "/var/log/php/php-error.log"
log_errors_max_len = 4096
ignore_repeated_errors = On
ignore_repeated_source = On
report_memleaks = On
html_errors = Off ; 純文字格式,利於日誌分析工具解析
搭配 logrotate 定期切割、ELK/EFK 集中收集、Sentry 即時告警,構建完整可觀測體系。
<?php
// 統一錯誤/異常入口
set_error_handler(function ($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) return false; // 尊重 error_reporting 層級
throw new ErrorException($message, 0, $severity, $file, $line);
});
set_exception_handler(function (Throwable $e) {
// 記錄完整堆疊
error_log(sprintf(
"[%s] %s in %s:%d\nStack:\n%s",
date('c'), $e->getMessage(), $e->getFile(), $e->getLine(), $e->getTraceAsString()
));
// 生產環境回傳友善頁面
if (ini_get('display_errors')) {
echo "系統暫時不可用
錯誤已記錄,請稍後再試。
";
} else {
http_response_code(500);
include __DIR__ . '/templates/500.html';
}
});
// 致命錯誤捕獲(register_shutdown_function)
register_shutdown_function(function () {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
// 交給 exception handler 統一處理
(new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']))->getMessage();
// 這裡可手動觸發自訂處理邏輯
}
});
?>
phpinfo() 確認 Loaded Configuration File、display_errors、error_log 實際值display_errors = Off、log_errors = On、error_log 指向可寫路徑ini_set('display_errors', 1) 於生產程式碼APP_DEBUG=false, Symfony APP_ENV=prod, ThinkPHP app_debug=false)錯誤處理不是「隱藏錯誤」,而是「讓對的人在對的時間看到對的資訊」。建立分級、可追蹤、可告警的錯誤體系,是專業 PHPer 的必修功課。