首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP register_shutdown_function 捕獲 Fatal Error 實作友善錯誤頁面

     編輯:PHP 相關知識

PHP register_shutdown_function 捕獲 Fatal Error 實作友善錯誤頁面

PHP 腳本發生 Fatal Error(如類別不存在、require 失敗、記憶體耗盡)時,若 display_errors=Off 使用者看到空白頁;若 On 則暴露敏感堆疊資訊。使用 register_shutdown_function 可在腳本關閉前攔截致命錯誤,輸出友善提示或記錄日誌。

核心機制

  • register_shutdown_function 註冊的回調函數會在腳本執行結束(正常結束或致命錯誤)時自動呼叫
  • 搭配 error_get_last() 可取得最後發生的錯誤資訊
  • 透過旗標變數區分「正常結束」與「異常中止」

基礎實作範例

<?php
$flag = false; // 腳本正常結束旗標

function deal_error() {
    global $flag;
    
    if (!$flag) {
        // 取得最後錯誤
        $error = error_get_last();
        
        if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
            // 記錄日誌(生產環境建議)
            error_log("[FATAL] {$error['message']} in {$error['file']}:{$error['line']}");
            
            // 輸出友善頁面(避免輸出敏感資訊)
            http_response_code(500);
            echo '
系統暫時無法服務

系統暫時無法服務

我們已記錄此錯誤,技術團隊正在處理中。

返回首頁

'; } } return false; } register_shutdown_function('deal_error'); // 業務邏輯區域 // $obj = new NotExistClass(); // 觸發 Fatal Error require './test.php'; // 檔案不存在觸發 Fatal Error $flag = true; // 正常結束標記 ?>

進階版:支援多種錯誤類型與 JSON 回應

class ErrorHandler {
    private static $completed = false;
    
    public static function register() {
        register_shutdown_function([__CLASS__, 'handleShutdown']);
        set_error_handler([__CLASS__, 'handleError']);
        set_exception_handler([__CLASS__, 'handleException']);
    }
    
    public static function markCompleted() {
        self::$completed = true;
    }
    
    public static function handleShutdown() {
        if (self::$completed) return;
        
        $error = error_get_last();
        if (!$error) return;
        
        $fatalTypes = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR];
        if (!in_array($error['type'], $fatalTypes)) return;
        
        self::respond($error, 500);
    }
    
    public static function handleError($severity, $message, $file, $line) {
        if (!(error_reporting() & $severity)) return false;
        
        $error = [
            'type' => $severity,
            'message' => $message,
            'file' => $file,
            'line' => $line
        ];
        
        // 非致命錯誤記錄日誌但不中斷
        error_log("[ERROR] {$message} in {$file}:{$line}");
        return true; // 不執行內建錯誤處理
    }
    
    public static function handleException(Throwable $e) {
        if (self::$completed) return;
        
        $error = [
            'type' => 'Exception',
            'message' => $e->getMessage(),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'trace' => $e->getTraceAsString()
        ];
        
        self::respond($error, 500);
    }
    
    private static function respond(array $error, int $code) {
        http_response_code($code);
        
        // AJAX 請求回傳 JSON
        if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && 
            strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode([
                'error' => true,
                'message' => '伺服器內部錯誤',
                'code' => $code
            ], JSON_UNESCAPED_UNICODE);
            return;
        }
        
        // 一般請求輸出 HTML
        // ... 輸出友善錯誤頁面
    }
}

// 入口文件最早引入
ErrorHandler::register();

// 業務代碼結束前
ErrorHandler::markCompleted();

適用與不適用場景

可捕獲 不可捕獲
類別不存在 new NotExistClass() 語法錯誤 (編譯階段失敗)
require/include 不存在檔案 超時中斷 max_execution_time(視 SAPI 而定)
記憶體耗盡 memory_limit Process 被 kill (OOM Killer、SIGKILL)
未捕獲的 Exception PHP 內核崩潰
觸發 E_USER_ERROR 語法錯誤如少分號、多括號

生產環境部署建議

  1. 入口文件最早處註冊:確保所有後續代碼都受保護
  2. 區分環境:開發環境顯示詳細錯誤,生產環境輸出友善頁面 + 記錄完整日誌
  3. 整合監控系統:將錯誤推送至 Sentry、Rollbar、或自建 ELK
  4. 避免在 shutdown 重新執行複雜邏輯:記憶體可能已耗盡,僅做輕量輸出
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved