关于PHP中的异常及错误处理
如果没有
异常及错误处理机制,我们处理这些问题的时候,需要采用if...else的机制,这会使我们的代码变得冗长且不利于阅读,这时候,我们需要一些异常或者错误让我们必须去处理,强迫我们解决这些问题或者是把这个问题抛出给能解决它的环境。这样我们就实现了正常执行过程的代码和出了问题进行处理代码的分离
异常和错误是两个不同的概念,但对于不同的程序开发语言,它们的概念是不同的。PHP中,遇到任何的错误都会触发一个错误,而不是抛出异常。但是在JAVA中,异常是处理错误的唯一方法。
在PHP中,如果需要使用异常,我们必须通过throw,主动抛出异常,它不会自动的抛出异常,这与java完全不同。
例如对于除0问题(5/0),php直接会触发一个warning错误,而不是抛出异常,但是java中会抛出一个arithmeticException。也就是说,php是不会自动捕捉有意义的异常,而是把所以不正常的情况都视为了错误。
但是java中就有一套完整的异常机制,内置很多异常类自动捕捉各种各样的异常。但php这个机制不完善。
下面我实现了一个php中简单的异常处理样例:
class mailException extends Exception{
public function __toString(){
return $this->getCode().' '.$this->getMessage().' '.$this->getFile().' '.$this->getLine();
}
}
try{
$mail="";
if(empty($mail))
throw new mailException('未填写mail');
}
catch(mailException $e){
echo $e;
}
下面介绍php中异常的使用场景:
- php程序员对程序的悲观预测
- 程序的需要和对业务的关注
- 语言级别的健壮性需求
对于2,更多的是对数据一致性的处理。比如上传文件和对数据库处理上,当上传了文件但数据库操作出现问题时,就需要抛出异常,并将上传的文件进行处理,回滚到这个业务处理之前。
对于异常,有两种处理方式,一种是捕获到之后立即处理(就是说在捕获和处理一个函数中),另一种是分散抛异常集中处理。
使用前一种的场景是,当业务很重要的时候。避免异常的扩散。后一种的使用场景是,异常不是那么重要,更强调了业务流程的走向的时候。
异常可以认为是一个内建的恢复系统,可以将系统恢复到某个稳定的点上,而这个点就是程序的上下文环境,而try块里的代码就保存了catch所要知道的程序上下文信息。所以如果很看重业务的话,应该分散的处理异常,也就是第一中方法。
对于3,与java相比,php的健壮性上是不足的。java是对于某些异常的处理的强制的。比如线程中断异常,程序员必须面对,不能回避。你可以什么都不做,直接抛出,但是你必须知道这个异常可能发生。这些机制,保证了java的健壮性。try..catch的好处就是可以讲异常造成的逻辑中断的破坏降低到最小的范围内,并通过补救措施处理后不影响业务逻辑的完整性。
手动抛出异常的意义不是很大,因为这意味着我们要完全的预知错误的出现,这不算异常,而是意料之中。
有没有办法让php主动抛出异常呢,有,那就是结合php的错误处理来抛出异常。这个一会会介绍到。
SPL中定义了一大堆的exception,但大多是一个空壳,并没有定义相应的方法,只是提供了一个命名规范。
PHP中的错误大致分为以下几类,其实一共定义了16个级别,常用的就是以下几个
- deprecated 不推荐级别
- notice 告诉你语法中存在不恰当的情况,比如变量未定义
- warning 语法中存在不恰当的情况 比如参数不匹配
- fetal error致命错误 这个错误发生时,会使php流程终结,不再继续执行
- parse error 语法解析错误 这个属于语法检查阶段的错误
php中可以使用set_error_hander来主动接管php中的错误处理,可以通过trigger_error来主动抛出一个错误
错误处理函数要包括参数列表为,errno,errstr,errfile,errline
可以通过restore_error_hander来取消托管。
测试代码如下:
function errorDeal($errno,$errstr,$errfile,$errline){
echo "[{$errno}]"."\r\n";
echo ${errstr}."\r\n";
echo $errfile."\r\n";
echo $errline."\r\n";
}
set_error_handler("errorDeal");
echo 5/0;
restore_error_handler();
5/0;
输入结果如下:
[2] Division by zero C:\xampp\xampp\htdocs\study\index.php 10
Warning: Division by zero in C:\xampp\xampp\htdocs\study\index.php on line 12
之前提到的可以结合php的错误处理来抛出异常,就是在错误处理函数中,抛出异常,然后通过try..catch捕获,这样的方式存在的问题是,完全需要程序员掌控对异常的处理,对于异常高发区,程序员处理不好的话,会造成数据不一致的后果。优点是可以获得程序运行的上下文环境,进行针对性的补救。
像fetal error这样的错误虽然捕获不到,但是可以通过特殊的方法处理。需要用到register_shutdown_function函数,这会在程序终止时调用注册的这个函数。(程序正常终止也会调用)
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 'test';
结果如下
Fatal error: Class 'a' not found in C:\xampp\xampp\htdocs\study\index.php on line 23
Array ( [type] => 1 [message] => Class 'a' not found [file] => C:\xampp\xampp\htdocs\study\index.php [line] => 23 ) stop
此外还可以通过trigger_error来主动抛出一个错误
代码如下
$mail;
if (empty($mail)) {
trigger_error("mall is null",E_WARNING);
}
结果如下
Warning: Invalid error type specified in C:\xampp\xampp\htdocs\study\index.php on line 28
总结一下:php中起初是没有异常机制,它是为了进军企业级开发所引入的,所以有了错误和异常并存的局面,这种形式导致php中异常处理不伦不类。在java中我们了解到了异常的真实含义。错误处理应该是对异常处理的一种补充