加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > PHP > 正文

PHP7中的异常与错误处理

发布时间:2020-05-26 01:36:38 所属栏目:PHP 来源:互联网
导读:PHP 中的 Exception, Error, Throwable PHP 中将代码自身异常(一般是环境或者语法非法所致)称作错误#160;Error,将运行中出现的逻辑错误称为异常#160;E

PHP 中的 Exception,Error,Throwable

  • PHP 中将代码自身异常(一般是环境或者语法非法所致)称作错误Error,将运行中出现的逻辑错误称为异常Exception
  • 错误是没法通过代码处理的,而异常则可以通过try/catch来处理
  • PHP7 中出现了Throwable接口,该接口由ErrorException实现,用户不能直接实现Throwable接口,而只能通过继承Exception来实现接口

PHP7 异常处理机制

过去的 PHP,处理致命错误几乎是不可能的。致命错误不会调用由set_error_handler()设置的处理方式,而是简单的停止脚本的执行。

在 PHP7 中,当致命错误和可捕获的错误(E_ERRORE_RECOVERABLE_ERROR)发生时会抛出异常,而不是直接停止脚本的运行。对于某些情况,比如内存溢出,致命错误则仍然像之前一样直接停止脚本执行。在 PHP7 中,一个未捕获的异常也会是一个致命错误。这意味着在 PHP5.x 中致命错误抛出的异常未捕获,在 PHP7 中也是致命错误。

注意:其他级别的错误如warningnotice,和之前一样不会抛出异常,只有fatalrecoverable级别的错误会抛出异常。

fatalrecoverable级别错误抛出的异常并非继承自Exception类。这种分离是为了防止现有 PHP5.x 的用于停止脚本运行的代码也捕获到错误抛出的异常。fatalrecoverable级别的错误抛出的异常是一个全新分离出来的类Error类的实例。跟其他异常一样,Error类异常也能被捕获和处理,同样允许在finally之类的块结构中运行。

Throwable

为了统一两个异常分支,ExceptionError都实现了一个全新的接口:Throwable

PHP7 中新的异常结构如下:

1 interface Throwable
2     |- Exception implements Throwable
3         |- ...
4     |- Error implements Throwable
5         |- TypeError extends Error
6         |- ParseError extends Error
7         |- ArithmeticError extends Error
8             |- DivisionByZeroError extends ArithmeticError
9         |- AssertionError extends Error

如果在 PHP7 的代码中定义了Throwable类,它将会是如下这样:

 1 interface Throwable{
 2     public function getMessage(): string;
 3     public function getCode(): int;
 4     public function getFile(): string;
 5     public function getLine(): int;
 6     public function getTrace(): array;
 7     public function getTraceAsString(): string;
 8     public function getPrevious(): Throwable;
 9     public function __toString(): string;
10 }

这个接口看起来很熟悉。Throwable规定的方法跟Exception几乎是一样的。唯一不同的是Throwable::getPrevious()返回的是Throwable的实例而不是Exception的。ExceptionError的构造函数跟之前Exception一样,可以接受任何Throwable的实例。

Throwable可以用于try/catch块中捕获ExceptionError对象(或是任何未来可能的异常类型)。记住捕获更多特定类型的异常并且对之做相应的处理是更好的实践。然而在某种情况下我们想捕获任何类型的异常(比如日志或框架中错误处理)。在 PHP7 中,要捕获所有的应该使用Throwable而不是Exception

1 try {
2 // Code that may throw an Exception or Error.
3 } catch (Throwable $t) {
4 // Handle exception
5 }

用户定义的类不能实现Throwable接口。做出这个决定一定程度上是为了预测性和一致性——只有ExceptionError的对象可以被抛出。此外,异常需要携带对象在追溯堆栈中创建位置的信息,而用户定义的对象不会自动的有参数来存储这些信息。

Throwable可以被继承从而创建特定的包接口或者添加额外的方法。一个继承自Throwable的接口只能被ExceptionError的子类来实现。

1 interface MyPackageThrowable extends Throwable {}
2 
3 class MyPackageException extends Exception implements MyPackageThrowable {}
4 
5 throw new MyPackageException();

Error

事实上,PHP5.x 中所有的错误都是fatalrecoverable级别的错误,在 PHP7 中都能抛出一个Error实例。跟其他任何异常一样,Error对象可以使用try/catch块来捕获。

1 $var = 1;
2 try {
3 $var->method(); // Throws an Error object in PHP 7.
4 } catch (Error $e) {
5 // Handle error
6 }

通常情况下,之前的致命错误都会抛出一个基本的Error类实例,但某些错误会抛出一个更具体的Error子类:TypeErrorParseError以及AssertionError

TypeError

当函数参数或返回值不符合声明的类型时,TypeError的实例会被抛出。

 1 function add(int $left,int $right){
 2 return $left + $right;
 3 }
 4 
 5 try {
 6 $value = add('left','right');
 7 } catch (TypeError $e) {
 8 echo $e->getMessage(),"n";
 9 }
10 
11 //Argument 1 passed to add() must be of the type integer,string given

ParseError

include/require文件或eval()代码存在语法错误时,ParseError会被抛出。

1 try {
2 require 'file-with-parse-error.php';
3 } catch (ParseError $e) {
4 echo $e->getMessage(),"n";
5 }

ArithmeticError

ArithmeticError在两种情况下会被抛出。一是位移操作负数位。二是调用intdiv()时分子是PHP_INT_MIN且分母是 -1 (这个使用除法运算符的表达式:PHP_INT_MIN / -1,结果是浮点型)。

1 try {
2 $value = 1 << -1;
3 catch (ArithmeticError $e) {
4 echo $e->getMessage();//Bit shift by negative number
5 }

DevisionByZeroError

intdiv()的分母是 0 或者取模操作 (%) 中分母是 0 时,DivisionByZeroError会被抛出。注意在除法运算符 (/) 中使用 0 作除数(也即xxx/0这样写)时只会触发一个 warning,这时候若分子非零结果是 INF,若分子是 0 结果是 NaN。

1 try {
2 $value = 1 % 0;
3 } catch (DivisionByZeroError $e) {
4 echo $e->getMessage();//Modulo by zero
5 }

AssertionError

assert()的条件不满足时,AssertionError会被抛出。

ini_set('zend.assertions',1);
1 ini_set('assert.exception',1);
2 
3 $test = 1;
4 
5 assert($test === 0);
6 
7 //Fatal error: Uncaught AssertionError: assert($test === 0)

只有断言启用并且是设置 ini 配置的zend.assertions = 1assert.exception = 1时,assert()才会执行并抛AssertionError

在你的代码中使用 Error

用户可以通过继承Error来创建符合自己层级要求的Error类。这就形成了一个问题:什么情况下应该抛出Exception,什么情况下应该抛出Error

Error应该用来表示需要程序员关注的代码问题。从 PHP 引擎抛出的Error对象属于这些分类,通常都是代码级别的错误,比如传递了错误类型的参数给一个函数或者解析一个文件发生错误。Exception则应该用于在运行时能安全的处理,并且另一个动作能继续执行的情况。

由于Error对象不应该在运行时被处理,因此捕获Error对象也应该是不频繁的。一般来说,Error对象仅被捕获用于日志记录、执行必要的清理以及展示错误信息给用户。

编写代码支持 PHP5.x 和 PHP7 的异常

为了在同样的代码中捕获任何 PHP5.x 和 PHP7 的异常,可以使用多个catch,先捕获Throwable,然后是Exception。当 PHP5.x 不再需要支持时,捕获Exceptioncatch块可以移除。

1 try {
2 // Code that may throw an Exception or Error.
3 } catch (Throwable $t) {
4 // Executed only in PHP 7,will not match in PHP 5.x
5 } catch (Exception $e) {
6 // Executed only in PHP 5.x,will not be reached in PHP 7
7 }

不幸的是,处理异常的函数中的类型声明不容易确定。当Exception用于函数参数类型声明时,如果函数调用时候能用Error的实例,这个类型声明就要去掉。当 PHP5.x 不需要被支持时,类型声明则可以还原为Throwable

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读