接管错误处理·set_error_handler()

<?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); // 指定脚本运行错误日志文件

$callable = set_error_handler(
    static function (int $errno, string $errstr, string $errfile, int $errline): bool {
        $errors = [];
        $errors[E_ERROR] = 'Error'; // E_ERROR = 1: int
        $errors[E_WARNING] = 'Warning'; // E_WARNING = 2: int
        $errors[E_PARSE] = 'Parse'; // E_PARSE = 4: int
        $errors[E_NOTICE] = 'Notice'; // E_NOTICE = 8: int
        $errors[E_CORE_ERROR] = 'Core Error'; // E_CORE_ERROR = 16: int
        $errors[E_CORE_WARNING] = 'Core Warning'; // E_CORE_WARNING = 32: int
        $errors[E_COMPILE_ERROR] = 'Compile Error'; // E_COMPILE_ERROR = 64: int
        $errors[E_COMPILE_WARNING] = 'Compile Warning'; // E_COMPILE_WARNING = 128: int
        $errors[E_USER_ERROR] = 'User Error'; // E_USER_ERROR = 256: int
        $errors[E_USER_WARNING] = 'User Warning'; // E_USER_WARNING = 512: int
        $errors[E_USER_NOTICE] = 'User Notice'; // E_USER_NOTICE = 1024: int
        $errors[E_STRICT] = 'Strict'; // E_STRICT = 2048: int
        $errors[E_RECOVERABLE_ERROR] = 'Recoverable Error'; // E_RECOVERABLE_ERROR = 4096: int
        $errors[E_DEPRECATED] = 'Deprecated'; // E_DEPRECATED = 8192: int
        $errors[E_USER_DEPRECATED] = 'User Deprecated'; // E_USER_DEPRECATED = 16384: int
        $errors[E_ALL] = 'All'; // E_ALL = 32767: int

        $errtype = $errors[$errno] ?? "Unknown($errno)";

        // 仿照标准错误处理程序的格式
        $data = "$errtype: $errstr in $errfile on line $errline";

        // 加上错误发生时间
        $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;

        // 这里可根据实际情况(生产环境还是开发环境)选择返回值
        // return false; // 返回false继续执行标准错误处理程序(在页面上输出错误信息,前提是error_reporting设置为报告错误)
        return true; // 返回true不再执行标准错误处理程序(屏蔽一切错误信息输出)
    }, E_ALL
);
unset($callable);

// test(); // 调用未定义函数会报500错误(Fatal error),致命错误并不会交由处理函数

echo $test1; // echo未定义变量会报Warning错误,因为是非致命错误,所以会交由处理函数

echo $test2; // echo未定义变量会报Warning错误,因为是非致命错误,所以会交由处理函数


//========== 总结 ==========//
// 1、需要注意的是set_error_handler()无法接管Fatal error,例如调用未定义函数则会直接报Fatal error,而不会交由处理函数,如果要接
//    管Fatal error可以使用register_shutdown_function(),事实上register_shutdown_function()不能称为接管,因为无论如何Fatal error都
//    是无法避免的,该函数的作用只是以让开发者在系统报Fatal error后执行一些自己的操作。
// 2、set_error_handler()主要用于生产环境记录非致命错误,因为生产环境必须时刻屏蔽一切错误信息输出,该函数可以帮助开发者把运行时的
//    错误记录下来,虽然不是致命错误,但有错误始终是个隐患。
// 3、handler函数的返回值(false/true)虽然可以控制是否继续执行标准错误处理程序(即是否在页面上输出错误信息),但它的控制效果受
//    error_reporting设置的影响,只有开启了错误报告返回值才有意义,如果关闭了错误报告那么无论返回什么都不会在页面上输出错误信息。
// 4、处理函数并不是异步执行,在函数体里执行echo、exit也是有效的。
// 5、如果多次出现非致命错误,程序也会按顺序多次调用处理函数,在处理途中如果执行exit操作将会中止后续的调用处理函数。

Copyright © 2024 码农人生. All Rights Reserved