PHP 預設錯誤輸出格式單一,難以滿足生產環境需求(記錄日誌、郵件通知、友善錯誤頁面)。透過 set_error_handler() 註冊自定義處理函數,可完全掌控錯誤處理流程。
<?php
// 開啟所有錯誤報告
error_reporting(E_ALL);
/**
* 自定義錯誤處理器
*
* @param int $errno 錯誤層級
* @param string $errstr 錯誤訊息
* @param string $errfile 發生錯誤的檔案
* @param int $errline 發生錯誤的行號
* @return bool true 表示已處理,不再執行內建處理器
*/
function customErrorHandler(int $errno, string $errstr, string $errfile, int $errline): bool {
// 定義錯誤層級標籤與處理策略
$config = [
E_ERROR => ['label' => 'Fatal Error', 'halt' => true, 'log' => true, 'notify' => true],
E_WARNING => ['label' => 'Warning', 'halt' => false, 'log' => true, 'notify' => false],
E_PARSE => ['label' => 'Parse Error', 'halt' => true, 'log' => true, 'notify' => true],
E_NOTICE => ['label' => 'Notice', 'halt' => false, 'log' => true, 'notify' => false],
E_CORE_ERROR => ['label' => 'Core Error', 'halt' => true, 'log' => true, 'notify' => true],
E_CORE_WARNING => ['label' => 'Core Warning', 'halt' => false, 'log' => true, 'notify' => false],
E_COMPILE_ERROR => ['label' => 'Compile Error', 'halt' => true, 'log' => true, 'notify' => true],
E_COMPILE_WARNING => ['label' => 'Compile Warning', 'halt' => false, 'log' => true, 'notify' => false],
E_USER_ERROR => ['label' => 'User Error', 'halt' => true, 'log' => true, 'notify' => true],
E_USER_WARNING => ['label' => 'User Warning', 'halt' => false, 'log' => true, 'notify' => false],
E_USER_NOTICE => ['label' => 'User Notice', 'halt' => false, 'log' => true, 'notify' => false],
E_STRICT => ['label' => 'Strict', 'halt' => false, 'log' => true, 'notify' => false],
E_RECOVERABLE_ERROR => ['label' => 'Catchable Error', 'halt' => true, 'log' => true, 'notify' => true],
E_DEPRECATED => ['label' => 'Deprecated', 'halt' => false, 'log' => true, 'notify' => false],
E_USER_DEPRECATED => ['label' => 'User Deprecated', 'halt' => false, 'log' => true, 'notify' => false],
];
$cfg = $config[$errno] ?? ['label' => 'Unknown', 'halt' => false, 'log' => true, 'notify' => false];
$msg = sprintf(
"[%s] %s in %s on line %d\n",
$cfg['label'],
$errstr,
$errfile,
$errline
);
// 記錄日誌
if ($cfg['log']) {
error_log($msg, 3, __DIR__ . '/logs/php_errors.log'); // 寫入檔案
}
// 開發環境顯示錯誤
if (isset($_SERVER['APP_ENV']) && $_SERVER['APP_ENV'] === 'development') {
echo "" . htmlspecialchars($msg) . "
";
}
// 發送通知(如 Email、Slack)
if ($cfg['notify']) {
// mail('admin@example.com', 'PHP Error Alert', $msg);
}
// 嚴重錯誤中止腳本
if ($cfg['halt']) {
// 可在這裡載入友善錯誤頁面
// include __DIR__ . '/templates/500.php';
exit(1);
}
return true; // 表示已處理,不再呼叫內建處理器
}
// 註冊處理器
set_error_handler('customErrorHandler');
// 測試觸發各類錯誤
trigger_error('這是使用者 Notice', E_USER_NOTICE);
trigger_error('這是使用者 Warning', E_USER_WARNING);
trigger_error('這是使用者 Error', E_USER_ERROR);
// trigger_error('這是 Parse Error', E_PARSE); // Parse Error 無法由 trigger_error 產生
?>
true 表示「已處理」,PHP 將不再執行內建錯誤處理;回傳 false 或無回傳則繼續內建流程。E_ERROR、E_PARSE、E_CORE_ERROR、E_CORE_WARNING、E_COMPILE_ERROR、E_COMPILE_WARNING 以及 set_error_handler() 註冊前發生的錯誤,無法被自定義處理器捕獲。需配合 register_shutdown_function() 捕獲致命錯誤。E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED。Error 例外,建議搭配 set_exception_handler() 統一處理。<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$log = new Logger('app');
$log->pushHandler(new StreamHandler(__DIR__ . '/logs/app.log', Logger::WARNING));
function modernErrorHandler($errno, $errstr, $errfile, $errline) {
global $log;
$log->error($errstr, [
'file' => $errfile,
'line' => $errline,
'level' => $errno,
'trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5)
]);
return true;
}
set_error_handler('modernErrorHandler');
?>
自定義錯誤處理器是建立健壯 PHP 應用的關鍵一環。結合日誌記錄、環境區分、通知機制與友善錯誤頁面,可大幅提升系統可觀測性與使用者體驗。