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

PHP register_shutdown_function() 關閉回調實戰:捕獲致命錯誤、資源清理

     編輯:PHP 相關知識

PHP register_shutdown_function() 完整指南:腳本結束時的最後防線

register_shutdown_function() 註冊的回調函數會在 PHP 腳本**正常結束**、**致命錯誤終止**、**超時中止**或**用戶強制停止**時執行。它是捕獲 E_ERROR、執行清理工作、記錄關鍵日誌的關鍵機制。本文詳細解析用法、應用場景與避坑要點。

基本語法與執行時機

<?php
// 註冊關閉回調
register_shutdown_function('shutdownHandler');
// 或
register_shutdown_function([$obj, 'method']);
// 或
register_shutdown_function(function() { /* ... */ });

// 腳本結束時自動呼叫 shutdownHandler()
?>

觸發場景:

  • 腳本正常執行完畢
  • die()/exit() 主動結束
  • 致命錯誤 (E_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR)
  • 執行超時 (max_execution_time)
  • 用戶瀏覽器停止載入 (連線中斷)
  • PHP-FPM 進程被殺 (SIGTERM, SIGKILL 可能不觸發)

核心應用:捕獲致命錯誤

<?php
// shutdown.php
header('Content-Type: text/html; charset=utf-8');

class ShutdownHandler {
    public function handle() {
        $error = error_get_last(); // 只能獲取最後一個錯誤
        if ($error !== null) {
            // 判斷是否為致命錯誤
            $fatalTypes = [E_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR, E_CORE_ERROR, E_COMPILE_ERROR];
            if (in_array($error['type'], $fatalTypes)) {
                echo '
';
                echo "捕獲致命錯誤:\n";
                print_r($error);
                echo '
'; // 記錄到檔案 (需用絕對路徑,因工作目錄可能已變更) $logFile = 'D:/practise/php/Error/error.txt'; $msg = date('Y-m-d H:i:s') . " FATAL: {$error['message']} in {$error['file']}:{$error['line']}\n"; file_put_contents($logFile, $msg, FILE_APPEND | LOCK_EX); } } die('腳本結束 - Shutdown handler 執行完畢'); } } // 註冊:注意需實例化後傳入陣列 register_shutdown_function([new ShutdownHandler(), 'handle']); // 故意觸發致命錯誤 echo md6(); // 未定義函數 -> E_ERROR ?>

執行輸出結果

Fatal error: Call to undefined function md6() in D:\practise\php\Error\shutdown.php on line 28

Array
(
    [type] => 1
    [message] => Call to undefined function md6()
    [file] => D:\practise\php\Error\shutdown.php
    [line] => 28
)
腳本結束 - Shutdown handler 執行完畢

D:/practise/php/Error/error.txt 內容:

2024-01-15 10:30:45 FATAL: Call to undefined function md6() in D:\practise\php\Error\shutdown.php:28

進階應用場景

1. 釋放資源 / 關閉連線

<?php
$lockFile = fopen('/tmp/app.lock', 'w');
flock($lockFile, LOCK_EX);

register_shutdown_function(function() use ($lockFile) {
    flock($lockFile, LOCK_UN);
    fclose($lockFile);
    @unlink('/tmp/app.lock');
});

// 業務邏輯...
?>

2. 輸出緩衝區處理 (配合 ob_start)

<?php
ob_start();
register_shutdown_function(function() {
    $content = ob_get_clean();
    // 統一後處理:壓縮、註入監控碼、寫入快取
    echo $content;
});
?>

3. 模擬 PHP 4 建構子 (歷史)

<?php
class LegacyClass {
    function LegacyClass() {
        register_shutdown_function([$this, '_destructor']);
    }
    function _destructor() {
        // 清理邏輯
    }
}
?>

4. 效能分析與慢查詢記錄

<?php
$startTime = microtime(true);
$startMem = memory_get_usage();

register_shutdown_function(function() use ($startTime, $startMem) {
    $time = microtime(true) - $startTime;
    $mem = memory_get_peak_usage() - $startMem;
    if ($time > 1.0 || $mem > 50 * 1024 * 1024) {
        error_log("SLOW REQUEST: {$_SERVER['REQUEST_URI']} Time: {$time}s Mem: " . ($mem/1024/1024) . "MB");
    }
});
?>

關鍵避坑指南

陷阱說明解決
工作目錄變更關閉時 CWD 可能變為系統根目錄檔案操作**必須使用絕對路徑**
無法輸出 HTTP 標頭關閉階段已過標頭發送期限僅能輸出 Body 內容或寫入檔案/日誌
物件已銷毀關閉時物件可能已 __destruct回調內不要依賴其他物件狀態
多次註冊順序按註冊順序**反向**執行 (LIFO)注意依賴順序,或用單一回調統籌
error_get_last() 限制只能取最後一個錯誤,且不含 E_PARSE配合 set_error_handler 記錄所有錯誤
CLI vs Web 差異CLI 無超時限制,Web 受 max_execution_time測試需在實際運行環境驗證

與 register_tick_function 對比

特性register_shutdown_functionregister_tick_function
觸發時機腳本結束一次每個 tick (declare(ticks=N) 控制)
用途清理、致命錯誤捕獲、統計效能分析、信號模擬、定期檢查
執行上下文關閉階段正常執行流中

現代替代方案

  • Composer 套件:symfony/error-handler、filp/whoops 提供漂亮錯誤頁、日誌整合
  • 框架內建:Laravel App\Exceptions\Handler、Symfony Kernel::terminate()
  • APM 工具:New Relic、Datadog、Elastic APM 自動捕獲異常與追蹤

register_shutdown_function() 是 PHP 底層的「最後防線」,理解其執行語境與限制,能在關鍵時刻完成資源守護與錯誤見證。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved