PHP自动捕获Exception,Fatal error等错误和异常
2017-05-16 22:08
543 查看
PHP代码运行时,常常会出现Parse error,Fatal error、Exception导致程序异常终止。对于Parse error,因为它一般出现在脚本的启动阶段,所以相对而言比较容易发现和定位问题。而对于Fatal error、Exception这种可能出现在程序执行过程中的任何阶段的报错,就相对比较麻烦,尤其是对于一些需要持续运行的服务程序,比如数据统计脚本之类,在某个无人职守的夜晚默默crash掉,常常令人很无语。而且不同的环境,PHP的log可能不一定完整,这时定位问题就比较麻烦,从而降低了系统整体的稳定性和可用性。
而如果我们能在程序出现Fatal error、Exception时进行自动捕获,并且将其写到日志中,或者进行报警,就能大大减轻运维工作量从而提升系统可用性。我们今天就针对这个问题做一下探讨。
如果你足够熟悉PHP手册,你会知道PHP有几个系统函数专门提供给用户进行异常捕获,即set_exception_handler() 和set_error_handler() 。来,我们简单测试一下。
执行结果如下图:
我们可以看到程序成功捕获到了Exception,接下来我们再试试set_error_handler()
执行结果如下图:
这时我们看到notice级别的error也被捕获到了,看起来貌似用这两个函数就能解决问题咯~~来,下面我们试一下捕获Fatal error
结果:
这时程序居然没有捕获到错误而直接抛出Fatal error后退出。。。WTF
来,我们先看看这个函数的定义:
我们发现这个函数对于报错回调的错误类型是有限制的,再看看文档里的说明:以下级别的错误不能由用户定义的函数来处理: E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,和在 调用 set_error_handler() 函数所在文件中产生的大多数
E_STRICT。而且如果错误发生在脚本执行之前(比如文件上传时),将不会 调用自定义的错误处理程序因为它尚未在那时注册。
这时再仔细研究手册,会发现PHP还提供了两个函数register_shutdown_function() 和error_get_last()。
其中register_shutdown_function()函数可实现当程序执行完成后执行的函数,其功能为可实现程序执行完成的后续操作。error_get_last()以数组的形式返回最后发生的错误
。该数组包含4个k-v:
下面我们继续通过demo来看:
结果:
register_shutdown_function()并没有运行成功,其原因是该函数只会在run-time阶段执行,而由于代码中的函数重复定义是在parse-time阶段就会中断的错误,故register_shutdown_function()并没有被调用。
我们稍微修改一下代码:
结果:
我们看到这时register_shutdown_function()被成功调用了,这是因为我们通过加入一个if-else流控制,实现了一个类似于闭包的机制,与外面的test名称不冲突,这时跳过了parse-time阶段进入run-time阶段抛出错误。除此之外,我们还需要注意:
1,register_shutdown_function()函数可重复调用,但执行的顺序与注册的顺序相同
2,如果在调用register_shutdown_function()函数之前有exit()函数调用,register_shutdown_function()函数将不能执行
3,PHP4后支持注册函数参数传递
4,在某些服务端,如Apache,当前目录在register_shutdown_function()函数中能够改变
5,register_shutdown_function()函数执行在headers发送之后
那么有没有什么比较通用的解决方案,可以让所有报错都被捕获呢?
来,我们看下面这个demo:
errorHandler.php
errorTest.php
errorInclude.php
我们直接执行errorInclude.php
结果:
我们发现虽然依旧有函数重复定义的问题,但是register_shutdown_function()却成功得到了调用,这是因为错误没有发生在运行文件errorInclude.php,而是发生在运行文件include的文件中。故也是成功进入了run-time阶段才报错。
所以呢,我们可以在日常的开发中,结合实际情况把有可能会出error的程序按照上面的方式进行封装,就可以自动截获所有的error啦。
而如果我们能在程序出现Fatal error、Exception时进行自动捕获,并且将其写到日志中,或者进行报警,就能大大减轻运维工作量从而提升系统可用性。我们今天就针对这个问题做一下探讨。
如果你足够熟悉PHP手册,你会知道PHP有几个系统函数专门提供给用户进行异常捕获,即set_exception_handler() 和set_error_handler() 。来,我们简单测试一下。
<?php //errorTest.php set_exception_handler("exceptionDeal"); function exceptionDeal($exception) { echo $exception->getMessage().PHP_EOL; } throw new Exception("I 'm a Exception", 1);
执行结果如下图:
我们可以看到程序成功捕获到了Exception,接下来我们再试试set_error_handler()
<?php set_error_handler("errorDeal"); function errorDeal($errno, $errstr, $errfile, $errline) { $message = <<<EOF "errno":$errno "errstr":$errstr "errfile":$errfile "errline":$errline EOF; echo $message.PHP_EOL; } echo $test;//该变量未定义,这时会报一个notice级别的error
执行结果如下图:
这时我们看到notice级别的error也被捕获到了,看起来貌似用这两个函数就能解决问题咯~~来,下面我们试一下捕获Fatal error
<?php set_error_handler("errorDeal"); function errorDeal($errno, $errstr, $errfile, $errline) { $message = <<<EOF "errno":$errno "errstr":$errstr "errfile":$errfile "errline":$errline EOF; echo $message.PHP_EOL; } test();//调用不存在的函数,这时会报一个Fatal error
结果:
这时程序居然没有捕获到错误而直接抛出Fatal error后退出。。。WTF
来,我们先看看这个函数的定义:
mixed set_error_handler ( callable $error_handler [, int $error_types = E_ALL | E_STRICT ] )
我们发现这个函数对于报错回调的错误类型是有限制的,再看看文档里的说明:以下级别的错误不能由用户定义的函数来处理: E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,和在 调用 set_error_handler() 函数所在文件中产生的大多数
E_STRICT。而且如果错误发生在脚本执行之前(比如文件上传时),将不会 调用自定义的错误处理程序因为它尚未在那时注册。
这时再仔细研究手册,会发现PHP还提供了两个函数register_shutdown_function() 和error_get_last()。
其中register_shutdown_function()函数可实现当程序执行完成后执行的函数,其功能为可实现程序执行完成的后续操作。error_get_last()以数组的形式返回最后发生的错误
。该数组包含4个k-v:
[type] - 错误类型 [message] - 错误消息 [file] - 发生错误所在的文件 [line] - 发生错误所在的行
下面我们继续通过demo来看:
<?php register_shutdown_function("error_handler"); function error_handler() { echo "I 'm a Fatal error!"; } function test(){} function test(){}// 函数重复定义,这时会报一个Fatal error
结果:
register_shutdown_function()并没有运行成功,其原因是该函数只会在run-time阶段执行,而由于代码中的函数重复定义是在parse-time阶段就会中断的错误,故register_shutdown_function()并没有被调用。
我们稍微修改一下代码:
<?php function error_handler() { echo "I 'm a Fatal error!"; } if(true) { function test(){} } function test(){}// 函数重复定义,这时会报一个Fatal error
结果:
我们看到这时register_shutdown_function()被成功调用了,这是因为我们通过加入一个if-else流控制,实现了一个类似于闭包的机制,与外面的test名称不冲突,这时跳过了parse-time阶段进入run-time阶段抛出错误。除此之外,我们还需要注意:
1,register_shutdown_function()函数可重复调用,但执行的顺序与注册的顺序相同
2,如果在调用register_shutdown_function()函数之前有exit()函数调用,register_shutdown_function()函数将不能执行
3,PHP4后支持注册函数参数传递
4,在某些服务端,如Apache,当前目录在register_shutdown_function()函数中能够改变
5,register_shutdown_function()函数执行在headers发送之后
那么有没有什么比较通用的解决方案,可以让所有报错都被捕获呢?
来,我们看下面这个demo:
errorHandler.php
<?php //errorHandler.php register_shutdown_function( "fatal_handler" ); set_error_handler("error_handler"); define('E_FATAL', E_ERROR | E_USER_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_RECOVERABLE_ERROR| E_PARSE ); //获取fatal error function fatal_handler() { $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); } } //获取所有的error function error_handler($errno,$errstr,$errfile,$errline) { $str=<<<EOF "errno":$errno "errstr":$errstr "errfile":$errfile "errline":$errline EOF; //获取到错误可以自己处理,比如记Log、报警等等 echo $str; }
errorTest.php
<?php //errorTest.php function test1() { } function test1() { }
errorInclude.php
include_once('errorHandler.php'); include_once('errorTest.php');
我们直接执行errorInclude.php
结果:
我们发现虽然依旧有函数重复定义的问题,但是register_shutdown_function()却成功得到了调用,这是因为错误没有发生在运行文件errorInclude.php,而是发生在运行文件include的文件中。故也是成功进入了run-time阶段才报错。
所以呢,我们可以在日常的开发中,结合实际情况把有可能会出error的程序按照上面的方式进行封装,就可以自动截获所有的error啦。
相关文章推荐
- php致命错误的异常捕获
- PHP捕获Fatal error错误的方法
- PHP 的异常处理、错误处理:error_reporting,try-catch,trigger_error,set_error_handler,set_exception_handler,regis
- laravel 5异常错误:FatalErrorException in Handler.php line 38的解决
- __autoload && set_hander php的错误异常处理机制以及自动加载机制
- xser_exception_handler -- xser php fr v0.1 自定义捕获异常信息
- php捕获异常错误
- 错误-终止应用程序由于未捕获的异常的nsinvalidargumentexception’,原因:“[:]:未知的UIView setImage选择器送到实例0x8d78d20”
- PHP 的异常处理、错误处理:error_reporting,try-catch,trigger_error,set_error_handler,set_exception_handler,register_shutdown_function
- PHP捕获Fatal error错误的方法
- PHP中PDO错误/异常(PDOException)处理
- PHP捕获Fatal error错误的方法
- J2CA0056I: 连接管理器接收到来自资源 jdbc/gywork 的资源适配器的致命连接错误。接收到的异常为 com.ibm.websphere.ce.cm.StaleConnectionException: JZ006: 捕获到 IO 例外:com
- PHP中PDO错误/异常(PDOException)处理
- PHP捕获Fatal error错误的方法
- PHP学习系列之错误处理和异常捕获
- 韩顺平php视频笔记80 81 错误日志 异常捕获
- php 写入文件并捕获错误
- 验证配置设置时发生错误,已引发类型为System.Runtime.InteropServices.COMException的异常。其他异常信息:系统找不到指定的路径
- Java基教--异常与错误区别 Error and Exception