PHP-异常类
PHP 异常与错误 —— Throwable
Throwable
官方文档地址: http://php.net/manual/en/class.throwable.php
PHP 支持版本: 7
PHP7 异常与错误处理机制 Exception, Error, Throwable
Throwable 是 PHP 7 中可以用作任何对象抛出声明的基本接口,包括 Expection (异常)和 Error (错误)。
1 | Throwable { |
请一定要注意,没有特殊说明:本例 PHP Version < 7
说起 PHP 异常处理,大家首先会想到 try-catch,那好,我们先看一段程序吧:有一个 test.php 文件,有一段简单的 PHP 程序,内容如下,然后命令行执行:php test.php
1 | 1 <?php |
我的问题是:这段程序能正确的捕捉到除 0 的错误信息吗?
如果你回答能,那你就把这篇文章看完吧!应该能学点东西。
本文章分 5 个部分介绍我的异常处理的理解:
一、异常与错误的概述
二、ERROR 的级别
三、PHP 异常处理中的黑科技
四、巧妙的捕获错误和异常
五、自定义异常处理和异常嵌套
六、PHP7 中的异常处理
一、异常与错误的概述
PHP 中什么是异常:
程序在运行中出现不符合预期的情况,允许发生(你也不想让他出现不正常的情况)但他是一种不正常的情况,按照我们的正常逻辑本不该出的错误,但仍然会出现的错误,属于逻辑和业务流程的错误,而不是编译或者语法上的错误。
PHP 中什么是错误:
属于 php 脚本自身的问题,大部分情况是由错误的语法,服务器环境导致,使得编译器无法通过检查,甚至无法运行的情况。warning、notice 都是错误,只是他们的级别不同而已,并且错误是不能被 try-catch 捕获的。
上面的说法是有前提条件的:
在 PHP 中,因为在其他语言中就不能这样下结论了,也就是说异常和错误的说法在不同的语言有不同的说法。在 PHP 中任何自身的错误或者是非正常的代码都会当做错误对待,并不会以异常的形式抛出,但是也有一些情况会当做异常和错误同时抛出(据说是,我没有找到合适的例子)。也就是说,你想在数据库连接失败的时候自动捕获异常是行不通的,因为这就不是异常,是错误。但是在 java 中就不一样了,他会把很多和预期不一致的行为当做异常来进行捕获。
PHP 异常处理很鸡肋?
在上面的分析中我们可以看出,PHP 并不能主动的抛出异常,但是你可以手动抛出异常,这就很无语了,如果你知道哪里会出问题,你添加 if else 解决不就行了吗,为啥还要手动抛出异常,既然能手动抛出就证明这个不是异常,而是意料之中。以我的理解,这就是 PHP 异常处理鸡肋的地方(不一定对啊)。所以 PHP 的异常机制不是那么的完美,但是使用过框架的同学都知道有这个情况:你在框架中直接写开头那段 php“自动”捕获异常的代码是可以的,这是为什么?看过源码的同学都知道框架中都会涉及三个函数:register_shutdown_function,set_error_handler,set_exception_handler 后面我会重点讲解着三个黑科技,通过这几个函数我们可以实现 PHP 假自动捕获异常和错误。
二、ERROR 的级别
只有熟悉错误级别才能对错误捕捉有更好的认识。 ERROR 有不同的错误级别,我之前的一篇文章中有写到:http://www.cnblogs.com/zyf-zhaoyafei/p/3649434.html
下面我再总结性的给出这几类错误级别:
1 | 1 Fatal Error:致命错误(脚本终止运行) |
由此可知有 5 类是产生 ERROR 级别的错误,这种错误直接导致 PHP 程序退出。
可以定义成:
1 ERROR = E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_PARSE
三、PHP 异常处理中的黑科技
前面提到框架中是可以捕获所有的错误和异常的,之所以能实现应该是使用了黑科技,哈哈!其实也不是什么黑科技,主要是三个重要的函数:
1:set_error_handler()
看到这个名字估计就知道什么意思了,这个函数用于捕获错误,设置一个用户自定义的错误处理函数。
1 | 1 <?php |
当程序出现错误的时候自动调用此方法,不过需要注意一下两点:第一,如果存在该方法,相应的 error_reporting()就不能在使用了。所有的错误都会交给自定义的函数处理。第二,此方法不能处理以下级别的错误:E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,set_error_handler() 函数所在文件中产生的 E_STRICT,该函数只能捕获系统产生的一些 Warning、Notice 级别的错误。
并且他有多种调用的方法:
1 | 1 <?php |
2:register_shutdown_function()
捕获 PHP 的错误:Fatal Error、Parse Error 等,这个方法是 PHP 脚本执行结束前最后一个调用的函数,比如脚本错误、die()、exit、异常、正常结束都会调用,多么牛逼的一个函数啊!通过这个函数就可以在脚本结束前判断这次执行是否有错误产生,这时就要借助于一个函数:error_get_last();这个函数可以拿到本次执行产生的所有错误。error_get_last();返回的信息:
[type] - 错误类型
[message] - 错误消息
[file] - 发生错误所在的文件
[line] - 发生错误所在的行
1 | 1 <?php |
通过这种方法就可以巧妙的打印出程序结束前所有的错误信息。但是我在测试的时候我发现并不是所有的错误终止后都会调用这个函数,可以看下面的一个测试文件,内容是:
1 | 1 <?php |
1 a.php 文件
2
6
7 b.php 文件
8 register_shutdown_function: Type:' . $error['type'] . ' Msg: ' . $error['message'] . ' in ' . $error['file'] . ' on line ' . \$error['line'] . '');
14 }
15 }
16 require 'a.php';
17 ?>
1 | 3:set_exception_handler() |
1 set_exception_handler: Exception: " . $exception->getMessage() . ''); 6 } 7 throw new Exception("zyf exception"); 8 ?>
1 | 四、巧妙的捕获错误和异常 |
1 getMessage(); 14 } 15 ?>
1 | 好了,试一下,会打印出: |
1 a.php 内容:
2
3 // 模拟 Fatal error 错误
4 //test();
5
6 // 模拟用户产生 ERROR 错误
7 //trigger_error('zyf-error', E_USER_ERROR);
8
9 // 模拟语法错误
10 var_dump(23+-+);
11
12 // 模拟 Notice 错误
13 //echo \$f;
14
15 // 模拟 Warning 错误
16 //echo '123';
17 //ob_flush();
18 //flush();
19 //header("Content-type:text/html;charset=gb2312");
20 ?>
21 b.php 内容:
22
23 error_reporting(0);
24 register_shutdown_function('zyfshutdownfunc');
25 function zyfshutdownfunc()
26 {
27 if ($error = error_get_last()) {
28 var_dump('register_shutdown_function: Type:' . $error['type'] . ' Msg: ' . $error['message'] . ' in ' . $error['file'] . ' on line ' . $error['line'] . '');
29 }
30 }
31
32 set_error_handler('zyferror');
33 function zyferror($type, $message, $file, $line)
34 {
35 var_dump('set_error_handler: ' . $type . ':' . $message . ' in ' . $file . ' on ' . \$line . ' line .
');
36 }
37
38 require 'a.php';
39 ?>
1 | 到此就可以解释开头的那个程序了吧,test.php 如果是单文件执行是不能捕获到错误的,如果你在框架中执行就是可以的,当然你按照我上面介绍的来扩展也是可以的。 |
1 getLine().' in ' . $this->getFile() 7 .': ' . $this->getMessage() . ' Must in (0 - 60)'; 8 } 9 } 10 11 $age = 10; 12 try { 13 $age = intval($age); 14 if($age > 60) { 15 throw new zyfException($age); 16 } 17 18 } catch (zyfException $e) { 19 echo $e->errorzyfMessage(); 20 21 } 22 ?>
1 | 2:异常嵌套 |
1 60) { 6 throw new zyfException($age); 7 } 8 9 if ($age <= 10 0) { throw new exception($age . ' must> 0'); 11 } 12 13 } catch (zyfException $e) { 14 echo $e->errorzyfMessage(); 15 16 } catch(Exception $e) { 17 echo \$e->getMessage(); 18 } 19 ?>=>
1 | 当然也可以在 catch 中再抛出异常给上层: |
1 60) { 7 throw new Exception($age); 8 } 9 10 } catch (Exception $e) { 11 throw new zyfException($age); 12 13 } 14 15 } catch (zyfException $e) { 16 echo \$e->errorzyfMessage(); 17 } 18 ?>
1 | 六、PHP7 中的异常处理 |
1 getMessage() . ' zyf'; 7 } 8 9 try { 10 test(); 11 12 } catch(Error $e) { 13 echo $e->getMessage() . ' zyf'; 14 } 15 ?>
因为 PHP7 实现了 throwable 接口,那么就可以使用第一个这种方式来捕获异常。又因为部分 Error 实现了接口,并且更多的 Error 变为可捕获的 Exception,那么就可以使用第二种方式来捕获异常。下面是在网上找的 PHP7 的异常层次树:
Throwable
Exception 异常
...
Error 错误
ArithmeticError 算数错误
DivisionByZeroError 除数为 0 的错误
AssertionError 声明错误
ParseError 解析错误
TypeError 类型错误
转载:
http://www.cnblogs.com/zyf-zhaoyafei/p/6928149.html