在语言级别上,通常具有许多错误处理模式,但这些模式往往建立在约定俗称的基础上,也就是说这些错误都是预知的。但是在大型程序中,如果每次调用都去逐一检查错误,会使代码变得冗长复杂,到处充斥着if……else,并且严重降低代码的可读性。而且人的因素也是不可信赖的,程序员可能并不会把这些问题当一回事,从而导致业务异常。在这种背景下,就逐渐形成了异常处理机制,或者强迫消除这些问题,或者把问题提交给能解决它的环境。这就把“描述在正常过程中做什么事的代码”和“出了问题怎么办的代码”进行分离。
PHP里有一套错误处理机制,可以使用set_error_handler接管PHP错误处理,也可以使用trigger_error函数主动抛出一个错误。
set_error_handler()函数设置用户自定义的错误处理函数。函数用于创建运行期间的用户自己的错误处理方法。它需要先创建一个错误处理函数,然后设置错误级别。语法如下:
set_error_handler(error_function, error_types)
参数描述如下:
error_function:规定发生错误时运行的函数。必需。
error_types:规定在哪个错误报告级别会显示用户定义的错误。可选。默认为”E_ALL”。
提示:如果使用该函数,会完全绕过标准PHP错误处理函数,如果有必要,用户定义的错误处理程序必须终止(die())脚本。
如果在脚本执行前发生错误,由于在那时自定义程序还没有注册,因此就不会用到这个自定义错误处理程序。这先实现一个自定义的异常异常处理函数,如代码清单1-21所示。
代码清单1-21 自定义的异常处理函数
function customError($errno, $errstr, $errfile, $errline){
echo '错误代码:[' . $errno . ']' . $errstr . '\r \n';
echo '错误所在的代码行:' . $errline . ',文件:' . $errfile . '\r \n';
die();
}
set_error_handler('customError', E_ALL | E_STRICT);
$a = array('o' => 2,4,6,8);
echo $a[o];
在这个函数里,可以对错误的详情进行格式化输出,也可以做任何要做的事情,比如判断当前环境和权限给出不同的错误提示,可使用error_log函数将错误记入log文件,还可以细化处理,针对errno的不同进行对应的处理。
自定义的错误处理函数一定要有这四个输入变量errno,errstr,errline,errfile。
errno是一组常量,代表错误的等级,同时也有一组整数和其对应,但一般使用其字符串值表示,这样语义更好一点。比如E_WARNING,其二进制掩码为4.,表示警告信息。
接下来,就是将这个函数作为回调参数传递给set_error_handler。这样就能接管PHP原生的错误处理函数了。要注意的是,这种托管方式并不能托管所有种类的错误,如E_ERROR、E_PARSE、E_CORE_ERROR、E_CORE_WARNING、E_COMPILE_ERROR、E_COMPILE_WARNING,以及E_STRICT中的部分。这些错误会以最原始的方式显示,或者不显示。
set_error_handler函数会接管PHP内置的错误处理,你可以在同一个页面使用restore_error_handler();取消接管。
注意:如果使用自定义的set_error_handler接管PHP的错误处理,先前代码里的错误抑制@将失效,这种错误也会被显示。
在PHP异常中,异常处理机制是有限的,无法自动抛出异常,必须手动进行,并且内置异常有限。PHP把许多异常看做错误,这样就可以把这些“异常”像错误一样用set_error_handler接管。进而主动抛出异常。代码如下所示:
function customError($errno, $errstr, $errfile, $errline){
//自定义错误处理时,手动抛出异常
throw new Exception($level . '|' $errstr);
}
set_error_handler('customError', E_ALL | E_STRICT);
try{
$a = 5/0;
}catch(Exception $e){
echo '错误信息:' . $e->getMessage();
}
这样就能捕获到异常和非致命的错误,可以弥补PHP异常处理机制的部分不足。
这种“曲折迂回”的处理方式存在的问题就是:必须依靠程序员自己来掌握对异常的处理,对于异常高发区,敏感区,如果程序员处理不好,就会导致前面所提到的业务数据不一致的问题。其优点在于,可以获得程序运行时的上下文信息,以进行针对性的补救。
fetal error这样的错误虽然捕获不到,也无法在发生此错误后恢复流程处理,但是还是可以使用一些特殊方法对这种错误进行处理的。这需要用到一个函数——register_shutdown_function,此函数会在PHP程序终止或者die时触发一个函数,给PHP来一个短暂的“回光返照”。在PHP4的时代,类不支持析构函数,常用这个函数模拟实现析构函数。实例代码如下:
class Shutdown{
public function stop(){
if (error_get_last()){
print_r(error_get_last());
}
die('Stop');
}
}
register_shutdown_function(array( new Shutdown(), 'stop'));
$a = new a(); // 将因为致命错误而失败
echo '必须终止';
可以运行看看效果。对于fetal_error还能做点收尾工作,但是PHP流程的终止是必然的。对于Parse error级别的错误,你只有傻眼了,除了可以修改配置文件php.ini,什么都做不了,修改的内容如下:
log_errors=On
error_log=usr/log/php.log
这样一旦PHP发生了错误,就会被记入log文件,方便以后查询。
和exception类似,错误处理也有对应抛出错误的函数,那就是trigger_error函数,如下所示:
$divisor=0;
if ($divisor == 0){
trigger_error('Cannot divide by zero', E_USER_ERROR);
}
echo 'break';
提示:在PHP中,错误和异常是两个不同的概念,这种设计从根本上导致了PHP的异常和其他语言相异。以Java为例,Java中,异常是错误唯一的报告方式。说到底,两者的区别就是对异常和错误的认识不同而产生的。PHP的异常绝大部分必须通过某种办法手动抛出,才能被捕获到,是一种半自动化的异常处理机制。
无论是错误还是异常,都可以使用handler接管系统已有的处理机制。