我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

在本文中,苏南大叔将对上一篇文章中的遗留问题进行进一步叙述。内容主要针对php5中,如何捕获fatal error而展开。

苏南大叔:php错误处理之set_error_handler增强版 - php错误处理之set_error_handler增强版
php错误处理之set_error_handler增强版(图4-1)

当然,在php7中,您可能需要的仅仅是set_exception_handler,用它捕获fatal error即可,不过本文的内容,对于php7也有一定的学习借鉴意义。上一篇文章的地址是:https://newsn.net/say/php-set_error_handler.html

前言

在上一篇的文章中,我们可以知道,在php7中,我们可以使用set_exception_handlerset_error_handler,完美捕获错误信息。而在php5中,似乎不是那么的完美,总是有些fatal error是不能捕获的。这里有个解决方案。苏南大叔称之为set_error_handler增强版

error的级别

在php的函数中,有个error_reporting的函数,它规定了什么样的级别的error才会report。从这里,我们可以知道error是有级别区别的。具体的信息可以点击这里:http://doc.php.sh/zh/function.error-reporting.html

而在上一篇文章中,trigger_error触发的error的级别是:notice,最后的未定义函数触发的error级别是:fatal error。那么对应的php级别名词是:notice=>E_USER_NOTICE,fatal error=>E_ERROR。具体的错误级别说明,可以点击这里查看:http://doc.php.sh/zh/errorfunc.constants.html

set_error_handler不处理E_ERROR

mixed set_error_handler ( callable $error_handler [, int $error_types = E_ALL | E_STRICT ] )
重要的是要记住 error_types 里指定的错误类型都会绕过 PHP 标准错误处理程序, 除非回调函数返回了 FALSE。 error_reporting() 设置将不会起到作用而你的错误处理函数继续会被调用 —— 不过你仍然可以获取 error_reporting 的当前值,并做适当处理。

以下级别的错误不能由用户定义的函数来处理: E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、E_COMPILE_ERROR、 E_COMPILE_WARNING,和在 调用 set_error_handler() 函数所在文件中产生的大多数 E_STRICT。

上述引用内容来自:http://doc.php.sh/zh/function.set-error-handler.html

苏南大叔的解读是:默认参数$error_types,将所有的错误信息,E_ALL | E_STRICT都默认绕过php标准错误处理程序,当且仅当回调函数返回false的时候,才会进入默认php标准错误处理程序。无视error_reporting()的相关设置。

从这里,我们就可以理解php5中,为什么最后的“函数未定义”没有被set_error_handler捕获了。这个“函数未定义”错误在php7中可以被set_exception_hanlder捕获。

php5中的解决方案

php5虽然已经是过去时了,但是下面的代码还是有点借鉴意义的。

代码的原理是:在一个单独的include文件里面,定义register_shutdown_function的回调函数,然后通过分析error_get_last()获得fatal error的信息,进而转向错误处理程序。当然,set_error_handler也会跟随设置。

两者区别就在于:错误处理回调函数,如果是error_get_last()后触发的,那么return false或者return true都没有意义了。因为是先触发的错误,而后进行捕获的。如果是set_error_handler触发的,那么是先捕获错误,然后再由return false或者return true来决定是否触发php标准错误处理程序。

对于error_get_last()触发的这种错误回调情况,我们需要使用error_reporting(0)或者@来抑止标准的错误输出。

代码如下:
error_handler.php:

//error_reporting(0);
register_shutdown_function("shutdown_handler");
function shutdown_handler() {
    define('E_FATAL', E_ERROR | E_USER_ERROR | E_CORE_ERROR |
        E_COMPILE_ERROR | E_RECOVERABLE_ERROR | E_PARSE);
    $error = error_get_last();
    if ($error && ($error["type"] === ($error["type"] & E_FATAL))) {
        $errno = $error["type"];
        $errfile = $error["file"];
        $errline = $error["line"];
        $errstr = $error["message"];
        error_handler($errno, $errstr, $errfile, $errline);
    }
}
set_error_handler("error_handler");
function error_handler($errno, $errstr, $errfile, $errline) {
    echo "<b>出错了,错误如下:</b><br/><br/>";
    echo "代码: $errno <br />\n";
    echo "信息: $errstr <br />\n";
    echo "文件: $errfile <br />\n";
    echo "行号: $errline <br />\n";
    return true;
    //return false;
}

index.php:

require "error_handler.php";
echo "PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
//set_error_handler("error_handler");
//throw new Exception('这是个自定义异常exception');
//throw new Exception('这是个带编号的自定义异常exception',1);
//trigger_error("这是个无法捕捉的自定义的错误error");
//throw new Error('这是个带编号的自定义的错误error',2);
@this_is_an_on_purpose_error();
echo "恭喜,没有错误\n";

苏南大叔:php错误处理之set_error_handler增强版 - 002
php错误处理之set_error_handler增强版(图4-2)

苏南大叔:php错误处理之set_error_handler增强版 - 003
php错误处理之set_error_handler增强版(图4-3)

方案效果截图

通过本增强版方案,php5捕获到了fatal error(代码为1)。

苏南大叔:php错误处理之set_error_handler增强版 - 009
php错误处理之set_error_handler增强版(图4-4)

结语

php错误处理,是php程序调试的关键因素之一。但是市面上的各种框架太多,调试方式也不一样。所以掌握背后的原理,就可以万变不离其宗,以不变应万变了。

更多苏南大叔的php相关经验总结,请点击这里查看:https://newsn.net/tag/php/

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   php    exception