PHP7中的异常与错误处理
PHP 中的 Exception, Error, Throwable
- PHP 中将代码自身异常(一般是环境或者语法非法所致)称作错误
Error
,将运行中出现的逻辑错误称为异常Exception
- 错误是没法通过代码处理的,而异常则可以通过
try/catch
来处理 - PHP7 中出现了
Throwable
接口,该接口由Error
和Exception
实现,用户不能直接实现Throwable
接口,而只能通过继承Exception
来实现接口
PHP7 异常处理机制
过去的 PHP,处理致命错误几乎是不可能的。致命错误不会调用由 set_error_handler()
设置的处理方式,而是简单的停止脚本的执行。
在 PHP7 中,当致命错误和可捕获的错误(E_ERROR
和 E_RECOVERABLE_ERROR
)发生时会抛出异常,而不是直接停止脚本的运行。对于某些情况,比如内存溢出,致命错误则仍然像之前一样直接停止脚本执行。在 PHP7 中,一个未捕获的异常也会是一个致命错误。这意味着在 PHP5.x 中致命错误抛出的异常未捕获,在 PHP7 中也是致命错误。
注意:其他级别的错误如
warning
和notice
,和之前一样不会抛出异常,只有fatal
和recoverable
级别的错误会抛出异常。
从 fatal
和 recoverable
级别错误抛出的异常并非继承自 Exception
类。这种分离是为了防止现有 PHP5.x 的用于停止脚本运行的代码也捕获到错误抛出的异常。fatal
和 recoverable
级别的错误抛出的异常是一个全新分离出来的类 Error
类的实例。跟其他异常一样,Error
类异常也能被捕获和处理,同样允许在 finally
之类的块结构中运行。
Throwable
为了统一两个异常分支,Exception
和 Error
都实现了一个全新的接口:Throwable
PHP7 中新的异常结构如下:
- interface Throwable
- |- Exception implements Throwable
- |- ...
- |- Error implements Throwable
- |- TypeError extends Error
- |- ParseError extends Error
- |- ArithmeticError extends Error
- |- DivisionByZeroError extends ArithmeticError
- |- AssertionError extends Error
如果在 PHP7 的代码中定义了 Throwable
类,它将会是如下这样:
- interface Throwable{
- public function getMessage(): string;
- public function getCode(): int;
- public function getFile(): string;
- public function getLine(): int;
- public function getTrace(): array;
- public function getTraceAsString(): string;
- public function getPrevious(): Throwable;
- public function __toString(): string;
- }
这个接口看起来很熟悉。Throwable
规定的方法跟 Exception
几乎是一样的。唯一不同的是 Throwable::getPrevious()
返回的是 Throwable
的实例而不是 Exception
的。Exception
和 Error
的构造函数跟之前 Exception
一样,可以接受任何 Throwable
的实例。
Throwable
可以用于 try/catch
块中捕获 Exception
和 Error
对象(或是任何未来可能的异常类型)。记住捕获更多特定类型的异常并且对之做相应的处理是更好的实践。然而在某种情况下我们想捕获任何类型的异常(比如日志或框架中错误处理)。在 PHP7 中,要捕获所有的应该使用 Throwable
而不是 Exception
。
- try {
- // Code that may throw an Exception or Error.
- } catch (Throwable $t) {
- // Handle exception
- }
用户定义的类不能实现 Throwable
接口。做出这个决定一定程度上是为了预测性和一致性——只有 Exception
和 Error
的对象可以被抛出。此外,异常需要携带对象在追溯堆栈中创建位置的信息,而用户定义的对象不会自动的有参数来存储这些信息。
Throwable
可以被继承从而创建特定的包接口或者添加额外的方法。一个继承自 Throwable
的接口只能被 Exception
或 Error
的子类来实现。
- interface MyPackageThrowable extends Throwable {}
- class MyPackageException extends Exception implements MyPackageThrowable {}
- throw new MyPackageException();
Error
事实上,PHP5.x 中所有的错误都是 fatal
或 recoverable
级别的错误,在 PHP7 中都能抛出一个 Error
实例。跟其他任何异常一样,Error
对象可以使用 try/catch
块来捕获。
- $var = 1;
- try {
- $var->method(); // Throws an Error object in PHP 7.
- } catch (Error $e) {
- // Handle error
- }
通常情况下,之前的致命错误都会抛出一个基本的 Error
类实例,但某些错误会抛出一个更具体的 Error
子类:TypeError
、ParseError
以及 AssertionError
。
TypeError
当函数参数或返回值不符合声明的类型时,TypeError
的实例会被抛出。
- function add(int $left, int $right){
- return $left + $right;
- }
- try {
- $value = add('left', 'right');
- } catch (TypeError $e) {
- echo $e->getMessage(), "\n";
- }
- //Argument 1 passed to add() must be of the type integer, string given
ParseError
当 include/require
文件或 eval()
代码存在语法错误时,ParseError
会被抛出。
- try {
- require 'file-with-parse-error.php';
- } catch (ParseError $e) {
- echo $e->getMessage(), "\n";
- }
ArithmeticError
ArithmeticError
在两种情况下会被抛出。一是位移操作负数位。二是调用intdiv()
时分子是 PHP_INT_MIN
且分母是 -1 (这个使用除法运算符的表达式:PHP_INT_MIN / -1
,结果是浮点型)。
- try {
- $value = 1 << -1;
- catch (ArithmeticError $e) {
- echo $e->getMessage();//Bit shift by negative number
- }
DevisionByZeroError
当 intdiv()
的分母是 0 或者取模操作 (%) 中分母是 0 时,DivisionByZeroError
会被抛出。注意在除法运算符 (/) 中使用 0 作除数(也即xxx/0这样写)时只会触发一个 warning,这时候若分子非零结果是 INF,若分子是 0 结果是 NaN。
- try {
- $value = 1 % 0;
- } catch (DivisionByZeroError $e) {
- echo $e->getMessage();//Modulo by zero
- }
AssertionError
当 assert()
的条件不满足时,AssertionError
会被抛出。
ini_set('zend.assertions', 1);
- ini_set('assert.exception', 1);
- $test = 1;
- assert($test === 0);
- //Fatal error: Uncaught AssertionError: assert($test === 0)
只有断言启用并且是设置 ini 配置的 zend.assertions = 1
和 assert.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 不再需要支持时,捕获 Exception
的 catch
块可以移除。
- try {
- // Code that may throw an Exception or Error.
- } catch (Throwable $t) {
- // Executed only in PHP 7, will not match in PHP 5.x
- } catch (Exception $e) {
- // Executed only in PHP 5.x, will not be reached in PHP 7
- }
不幸的是,处理异常的函数中的类型声明不容易确定。当 Exception
用于函数参数类型声明时,如果函数调用时候能用 Error
的实例,这个类型声明就要去掉。当 PHP5.x 不需要被支持时,类型声明则可以还原为 Throwable
。
PHP7中的异常与错误处理的更多相关文章
- PHP中的异常和错误(转载)
博客好久没有更新了,实在惭愧,最近在忙人生大事,哈哈!这段时间没有看什么新的东西,结合项目中遇到的PHP异常处理问题,我又重新梳理了之前模糊的概念,希望对大家理解PHP异常处理有所帮助. 请一定要注意 ...
- 深入探讨Java中的异常与错误处理
Java中的异常处理机制已经比较成熟,我们的Java程序到处充满了异常的可能,如果对这些异常不做预先的处理,那么将来程序崩溃就无从调试,很难找到异常所在的位置.本文将探讨一下Java中异常与错误的处理 ...
- Python基础之:Python中的异常和错误
目录 简介 Python中的内置异常类 语法错误 异常 异常处理 抛出异常 异常链 自定义异常 finally 简介 和其他的语言一样,Python中也有异常和错误.在 Python 中,所有异常都是 ...
- ionic创建工程中遇到异常、错误及解决方法
1. 创建工程——download failed ionic start myApp tabs 遇到如下错误 Downloading--Failed! Error:Timeout of 25000ms ...
- php7中异常
php7中新增异常错误处理 在PHP7之前的版本,对于一些错误异常是没有办法捕获的. php7中新增throwable接口,可以用来捕获一些错误 Exception,Error这实现了Throwabl ...
- 第33节:Java面向对象中的异常
Java中的异常和错误 Java中的异常机制,更好地提升程序的健壮性 throwable为顶级,Error和Exception Error:虚拟机错误,内存溢出,线程死锁 Exception:Runt ...
- ASP.NET Core 中间件Diagnostics使用 异常和错误信息
ASP.NET Core 中间件(Middleware)Diagnostics使用.对于中间件的介绍可以查看之前的文章ASP.NET Core 开发-中间件(Middleware). Diagnost ...
- PHP7中异常与错误处理与之前版本对比
PHP7中异常与错误处理与之前版本对比 先上代码 ECHO PHP_VERSION.PHP_EOL; function add (int $left,int $right){ return $left ...
- PHP如何进行错误与异常处理(PHP7中的异常处理和之前版本异常处理的区别)
PHP如何进行错误与异常处理(PHP7中的异常处理和之前版本异常处理的区别) 一.总结 一句话总结: throwable接口+Error类 在PHP7更新中有一条:更多的Error变为可捕获的Exce ...
随机推荐
- 攻防世界(XCTF)WEB(进阶区)write up(二)
国庆就要莫得了 起床刷几道 NewsCenter mfw Training-WWW-Robots NaNNaNNaNNaN-Batman bug NewsCenter search传参那里发现有注 ...
- oracle数据库的安全测试
Oracle Database,又名Oracle RDBMS,或简称Oracle.是甲骨文公司的一款关系数据库管理系统.它是在数据库领域一直处于领先地位的产品.可以说Oracle数据库系统是目前世界上 ...
- Struts2:搭建原理
记录下,struts2的搭建过程: 1核心jar包: struts-2.1.8\apps\struts2-blank-2.1.8.war 解压后 在struts2-blank-2.1.8\WEB-IN ...
- Cocos2d-x 学习笔记(11.6) Sequence
1. Sequence 动作序列.动作按参数顺序执行,动作总时长为每个动作的时长之和. 1.1 成员变量 FiniteTimeAction *_actions[]; float _split; // ...
- win10系统plsql卡顿、菜单闪烁解决办法
右键快捷方式--属性--兼容性: 设置为以win7模式运行,以管理员模式运行.如图:
- go-关键字-变量
var 声明变量 const 常量的关键字, 常量不能出现只声明不赋值的情况. 名字首字母为大写的程序实体可以被任何代码包中的代码访问到. 名字首字母为小写的程序实体则只能被同一个代码包中的代 ...
- 微信小程序之页面引用utils中的js文件
/* 只可使用相对路径 */const utils = require('../../../utils/util.js') Page({})
- 【python数据分析实战】电影票房数据分析(二)数据可视化
目录 图1 每年的月票房走势图 图2 年票房总值.上映影片总数及观影人次 图3 单片总票房及日均票房 图4 单片票房及上映月份关系图 在上一部分<[python数据分析实战]电影票房数据分析(一 ...
- 实战SpringCloud响应式微服务系列教程(第八章)构建响应式RESTful服务
本文为实战SpringCloud响应式微服务系列教程第八章,讲解构建响应式RESTful服务.建议没有之前基础的童鞋,先看之前的章节,章节目录放在文末. 1.使用springboot2.1.4构建RE ...
- 【java基础】- java双亲委派机制
在了解双亲委派机制之前,你应当知道classloader(如果不了解,可以现在去恶补一下哈) 四种classloader 虚拟机自带 引导类加载器(Bootstrap ClassLoader) 扩展类 ...