缺乏錯誤處理的代碼不僅不專業,更會暴露敏感路徑、資料庫結構等資訊,成為攻擊者的切入點。完善的錯誤處理能提升用戶體驗、保障系統安全、便於開發者排查問題。
最簡單的方式是在高風險操作前預判,失敗時輸出友善訊息並終止腳本。
<?php
// 檢查文件是否存在再打開
if (!file_exists('welcome.txt')) {
die('檔案不存在:welcome.txt');
}
$file = fopen('welcome.txt', 'r');
?>
優點:簡單直接。缺點:只能處理預期錯誤,無法捕捉運行時異常;終止腳本過於粗暴,不利於資源釋出與優雅降級。
PHP 允許註冊自定義函數接管所有錯誤(除 E_ERROR、E_PARSE 等致命錯誤外),實現統一格式化、記錄、通知。
function customError(int $errno, string $errstr, string $errfile = '', int $errline = 0, array $errcontext = []) : void
參數對應:錯誤級別、錯誤訊息、發生文件、行號、當時變量上下文(慎用,可能包含敏感數據)。
| 值 | 常量 | 說明 |
|---|---|---|
| 2 | E_WARNING | 非致命運行時警告,腳本繼續 |
| 8 | E_NOTICE | 運行時提示,潛在問題 |
| 256 | E_USER_ERROR | 用戶觸發的致命錯誤 |
| 512 | E_USER_WARNING | 用戶觸發的警告 |
| 1024 | E_USER_NOTICE | 用戶觸發的提示 |
| 4096 | E_RECOVERABLE_ERROR | 可捕獲的致命錯誤 |
| 8191 | E_ALL | 所有錯誤與警告 |
<?php
// 1. 定義處理器
function customError($errno, $errstr, $errfile, $errline) {
$log = "[ERROR] [$errno] $errstr in $errfile:$errline";
error_log($log, 3, __DIR__ . '/error.log'); // 寫入文件
// 生產環境不直接輸出,改為友善頁面
if (ENV_PRODUCTION) {
include '500.html';
exit;
}
echo "<b>$log</b><br>";
}
// 2. 註冊為預設處理器(處理所有級別)
set_error_handler('customError', E_ALL);
// 3. 手動觸發測試
$test = 2;
if ($test > 1) {
trigger_error('數值必須 ≤ 1', E_USER_WARNING);
}
?>
關鍵錯誤(如資料庫連接失敗、支付回調異常)需即時通知運維人員。
function criticalErrorHandler($errno, $errstr) {
$msg = "Critical: [$errno] $errstr";
error_log($msg, 1, 'ops@example.com', 'From: system@example.com'); // 發送郵件
error_log($msg, 3, '/var/log/php/critical.log'); // 同時落盤
die('系統繁忙,請稍後再試');
}
set_error_handler('criticalErrorHandler', E_USER_ERROR | E_RECOVERABLE_ERROR);
PHP 7+ 引入 Throwable 介面,Error 與 Exception 均實現該介面。建議採用 try-catch-finally 結構處理可預期的業務異常,配合 set_exception_handler() 捕獲未捕獲異常。
<?php
// 全局異常處理器
set_exception_handler(function (Throwable $e) {
http_response_code(500);
error_log($e->getMessage() . '\n' . $e->getTraceAsString(), 3, '/var/log/php/exception.log');
// 生產環境返回通用錯誤頁
include 'error_500.html';
});
// 業務代碼中拋出異常
function transfer($from, $to, $amount) {
if ($amount <= 0) throw new InvalidArgumentException('金額必須大於 0');
// ... 數據庫操作 ...
if ($dbError) throw new RuntimeException('轉賬失敗:' . $dbError);
}
try {
transfer(1, 2, 100);
} catch (InvalidArgumentException $e) {
// 參數錯誤,返回 400
} catch (RuntimeException $e) {
// 系統錯誤,記錄並返回 500
}
error_reporting(E_ALL); ini_set('display_errors', '1'); 全部顯示。display_errors=Off; log_errors=On; error_log=/var/log/php/error.log 隱藏細節,記錄日誌。register_shutdown_function() 最後防線。建立規範的錯誤處理體系,是從「能跑通」邁向「工程級可維護」的關鍵一步。