接管错误处理·register_shutdown_function()

<?php
declare(strict_types=1);
ini_set('display_errors', 'Off'); // 关闭打印错误信息
ini_set('error_reporting', E_ALL & ~E_DEPRECATED & ~E_STRICT); // 报告部分级别错误
ini_set('display_startup_errors', 'Off'); // 不显示启动过程中的错误
ini_set('log_errors', 'Off'); // 不记录脚本运行错误
ini_set('error_log', null); // 指定脚本运行错误日志文件

register_shutdown_function(static function (): void {
    $error = error_get_last(); // 获取最后发生的错误,若无错误则返回null
    // 说明①:若有多个错误,只会获取到最后一个
    // 说明②:可能是致命错误也可能是非致命错误(即不区分错误类型)

    // 只处理类型为E_ERROR的错误(即致命错误)
    if (is_array($error) && isset($error['type']) && $error['type'] === E_ERROR) {
        // 仿照标准错误处理程序的格式
        $data = "Fatal error: {$error['message']} in {$error['file']} on line {$error['line']}";

        // 加上错误发生时间
        $data = '[' . date('Y-m-d H:i:s') . "] $data" . PHP_EOL;

        // 错误日志文件
        $filename = __DIR__ . '/error.log';

        // 将错误信息写入错误日志文件
        file_put_contents($filename, $data, FILE_APPEND | LOCK_EX);

        // 处理函数也可以使用exit()停止脚本执行
        // exit('----- exit -----' . PHP_EOL);

        // 处理函数里的echo操作也是有效的,内容也会在页面上输出
        echo '----- echo -----' . PHP_EOL;
    }
});

test();  // 调用未定义函数会报500错误(Fatal error)


//========== 总结 ==========//
// 1、脚本正常执行完毕、执行exit、发生致命错误等出现程序中止的情况都会触发回调,所以需要通过error_get_last()判断是否发生错误。
// 2、若程序出现Fatal error,会先执行标准错误处理(即在页面输出错误信息),这是无法跳过的,然后才会调用开发者注册的回调函数。
// 3、register_shutdown_function()主要用于生产环境记录Fatal error,因为生产环境必须时刻屏蔽一切错误信息输出,如果生产环境出现
//    Fatal error页面只会报500错误,开发者不知道发生错误的文件和行号就会很难排错,而register_shutdown_function()则可以帮开发者
//    把详细的错误信息记录下来。
// 4、处理函数并不是异步执行,在函数体里执行echo、exit也是有效的。
// 5、和set_error_handler()不同,register_shutdown_function()注册的回调函数会且只会执行一次。
// 6、对于常驻内存程序(如使用Swoole等)使用register_shutdown_function()是没有意义的,即便发生了致命错误也不会执行回调函数。

Copyright © 2025 码农人生. All Rights Reserved