php面向对象异常处理,PHP 错误和异常处理(下)

PHP 错误和异常处理(下)

由 学院君 创建于9个月前, 最后更新于 7个月前

版本号 #1

1723 views

2 likes

0 collects

上篇我们讲了 PHP 中的错误报告和捕获,今天,我们来看看 PHP 程序中的异常处理。

错误 vs. 异常

错误与异常可以看作一对孪生兄弟,从严格的面向对象编程角度来说,错误指的是致命错误(Fatal Error,比如编译错误和语法错误),出现运行时错误后,程序应该无法继续往后执行,需要执行一些清理工作并记录日志后退出当前处理流程。

而异常指的是程序中出现的可预测的、可恢复的中轻度问题,比如数空对象引用、文件不存在、除数为零、数组越界等,当程序运行时出现异常后,我们可以对其进行捕获,或者抛给上层的业务代码处理,和错误报告类似,如果通过 set_exception_hanlder 函数定义了全局异常处理器,则所有未处理异常会集中到这里处理,如果没有定义任何处理异常的代码,最终会抛出一个 Fatal Error(也就是说,所有未处理异常都会被当作错误进行兜底处理)。程序出现异常后,应该可以继续往后执行。

但是我们在 PHP 中可以看到两者的边界并不明显,因为异常是 PHP 5 之后实现完整面向对象机制后引入的,之前的 PHP 中只有错误,没有异常,所以你可以看到那么多的错误级别,比如 Notice、Warning、Deprecated 这些中轻度错误,实际上完全可以通过异常进行处理。

层次结构

在 PHP 7 中,所有错误都归属于 Error 类,所有异常都归属于 Exception 类,两者是并列关系,并且最新 PHP 内置错误和异常类型如下表所示:

d9bff10f53719fd348a406c0a95a8fb8.png

而 Error 和 Exception 类又都实现了 Throwable 接口。

异常处理

有了以上的了解,大家应该大体上明白了异常是怎么回事以及所处的位置,接下来,我们来看看如何处理异常,我们按照三个层级递进:首先是在定义代码的地方捕获并处理,然后是在上层调用的地方捕获并处理,以及定义全局异常处理器处理。

在 php_learning/oop 目录下新建 exception.php 保存本篇教程的代码。

捕获异常

首先来看如何在代码定义的地方捕获异常,和错误捕获一样,我们可以 try...catch... 语句块捕获异常。

在 exception.php 中编写一段测试代码:

ae62700eeb18477c86bdada650971a47.png

我们试图从 $book 数组中访问一个不存在的索引,此时没有定义任何异常捕获和处理逻辑,所以会以错误报告方式进行兜底处理:

31ad84d738e245076905b128b743c811.png

现在我们在 getItemFromBook 方法中会参数进行验证,如果不满足要求则抛出异常:

function getItemFromBook($book, $key)

{

if (empty($book) || !key_exists($key, $book)) {

throw new InvalidArgumentException("数组为空或者对应索引不存在!");

}

return $book[$key];

}

通过 throw 关键字即可抛出异常,这里我们通过 new 关键字实例化了一个内置的 InvalidArgumentException 异常对象作为返回值抛出。

抛出异常后会终止后续代码的执行,然后我们可以在调用的地方通过 try/catch 对这个异常进行捕获:

try {

$val = getItemFromBook($book, 'desc');

} catch (InvalidArgumentException $exception) {

echo $exception->getMessage();

exit();

}

var_dump($val);

其原理是当 try 语句块中遇到异常后,会通过 catch 语句进行捕获,如果抛出的异常和声明异常类型匹配,则执行 catch 语句块中的内容。这样,当我们再次执行代码时,就会捕获这个异常:

38b330d07cba8f641565a0191c923537.png

如果你不知道抛出的异常类型是什么,可以通过 Exception 基类捕获(或者其他父级异常类),也就是说,此处也符合父子类型的转化逻辑:

try {

$val = getItemFromBook($book, 'desc');

} catch (Exception $exception) {

echo $exception->getMessage();

exit();

}

var_dump($val);

但是如果不是 InvalidArgumentException 或者其父类,就不能捕获了:

try {

$val = getItemFromBook($book, 'desc');

} catch (RuntimeException $exception) {

echo $exception->getMessage();

exit();

}

var_dump($val);

执行上述代码,打印结果如下:

b69a7952dab0b74645a9dd230f811b7f.png

未处理异常会转化为 Fatal Error 处理。

如果调用程序抛出了多个异常:

function getItemFromBook($book, $key)

{

if (empty($book)) {

throw new InvalidArgumentException("数组为空!");

}

if (!key_exists($key, $book)) {

throw new OutOfBoundsException("对应索引不存在!");

}

return $book[$key];

}

可以通过多个 catch 语句进行捕获:

try {

$val = getItemFromBook($book, 'desc');

} catch (InvalidArgumentException $exception) {

echo $exception->getMessage();

exit();

} catch (OutOfBoundsException $exception) {

echo $exception->getMessage();

exit();

}

var_dump($val);

但是由于我们在每个 catch 分支里面都调用 exit() 退出程序,可以通过添加 finally 语句块定义一个兜底逻辑:

$exit = false;

try {

$val = getItemFromBook($book, 'desc');

} catch (InvalidArgumentException $exception) {

echo $exception->getMessage() . PHP_EOL;

$exit = true;

} catch (OutOfBoundsException $exception) {

echo $exception->getMessage() . PHP_EOL;

$exit = true;

} finally {

$exit ? exit() : var_dump($val);

}

不管 try 语句块中的代码是否抛出异常,finally 语句块中的代码都会执行,如果抛出异常,则会先执行 catch 语句块中的代码,再执行 finally 语句块中的代码,否则会直接执行 finally 语句块中的代码。

抛出异常

我们也可以在捕获到异常后不进行处理,直接抛出,交给上一层调用代码进行进一步处理:

try {

$val = getItemFromBook([], null);

$val = getItemFromBook($book, 'desc');

} catch (InvalidArgumentException $exception) {

throw $exception;

} catch (OutOfBoundsException $exception) {

throw $exception;

} finally {

var_dump($val);

}

上一层的处理逻辑也无非是进行 try...catch... 捕获后进行处理或者继续抛出。

全局异常处理器

在进行系统框架设计时,考虑到系统的稳健型,总会有一些异常的「漏网之鱼」没有被捕获和处理,这个时候就要通过 set_exception_handler 函数注册全局的异常处理器来处理这些未被捕获和处理的异常:

...

function myExceptionHandler(Exception $exception)

{

echo 'Uncaught Exception [' . get_class($exception) . ']: ' . $exception->getMessage() . PHP_EOL;

echo 'Thrown in ' . $exception->getFile() . ' on line ' . $exception->getLine() . PHP_EOL;

}

set_exception_handler('myExceptionHandler');

try {

$val = getItemFromBook($book, 'desc');

} catch (InvalidArgumentException $exception) {

throw $exception;

} catch (OutOfBoundsException $exception) {

throw $exception;

} finally {

if (isset($val)) {

var_dump($val);

} else {

echo '异常将通过全局异常处理器处理...' . PHP_EOL;

}

}

我们首先需要定义一个自定义的 myExceptionHandler 函数作为全局异常处理器,在这个函数中,我们需要传入异常对象作为参数,然后输出该异常类名、消息、出现异常的文件和行号,最后通过 set_exception_handler 函数将其注册为全局异常处理器。

在后续调用 getItemFromBook 时,由于捕获的异常抛给了上一层,但目前没有上一层调用代码,也就变成了未处理异常,最终这些异常会通过全局异常处理器进行兜底处理,执行上述代码,输出如下:

29fc4429b831f432c887b50d09c705d9.png

这里是将异常信息输出到了标准输出(STDOUT),如果是在线上生产环境,和自定义的全局错误处理器一样,你也可以将这些信息记录到日志文件中,或者发送到第三方日志处理服务。

自定义异常类

上面所有的异常都是 PHP 内置的异常类,除此之外,我们也可以根据需要创建自定义的异常类,只需要继承自 Exception 基类或者其子类即可,比如我们为索引不存在定义一个独立的异常类,并且继承自 LogicException 父类:

class IndexNotExistsException extends LogicException

{

}

暂时不需要编写任何方法,它可以继承祖先类 Exception 的所有 protected/public 方法和属性:

4836e2e6b484648d668d901446e35334.png

需要注意的是,Exception 类中的很多方法定义前面都有一个 final 关键字,通过该关键字修饰的方法不能被子类重写,如果我们试图这么做会报错:

3caad4d6e9793d9e56cd34d26b086033.png

另外,final 还可以用于修饰类,通过 final 修饰的类将不能被子类继承。

定义好自定义类之后,就可以在代码中捕获和处理了:

function getItemFromBook($book, $key)

{

...

if (!key_exists($key, $book)) {

throw new IndexNotExistsException("对应索引不存在!");

}

...

}

...

try {

$val = getItemFromBook($book, 'desc');

} catch (InvalidArgumentException $exception) {

throw $exception;

} catch (IndexNotExistsException $exception) {

throw $exception;

} finally {

if (isset($val)) {

var_dump($val);

} else {

echo '异常将通过全局异常处理器处理...' . PHP_EOL;

}

}

执行上述代码,输出结果如下:

06cc3c9279d0ebe43714a149b346495c.png

说明自定义异常类已经可以正常使用。

在实际项目开发中,可以结合自定义异常类和上述异常处理方式构建自己的异常处理体系。

小结

关于 PHP 面向对象编程我们就简单介绍到这里,通过前面的介绍,相信你已经对类和对象的实例化,类级别的静态方法,类功能的垂直扩展(继承、抽象类、接口)和水平扩展(对象组合、Trait)有了充分的认识,此外,PHP 类还支持特有的魔术方法,合理使用这些魔术方法可以进行一些很方便的初始化/善后清理工作,最后,对于程序中出现的错误和异常,可以通过一系列内置的机制进行捕获和处理。

下篇教程,我们将开始介绍 PHP 中如何连接 MySQL 数据库并进行增删改查操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/558043.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电脑运行java游戏,电脑运行软件卡顿?这几招游戏或是办公,让你速度飞起!...

许多朋友想知道他们已经购买了高端计算机,但是无论玩游戏还是运行软件,他们仍然陷于困境。今天,让我们教大家一个简单的方法来提高计算机性能。焦点:此方法大大提高了用户对高度配置的计算机的影响。如果计算机的设置不太高&#…

c matlab 数据类型,matlab中数据类型与c语言数据类型的区别?

matlab中数据类型与c语言数据类型的区别?mip版 关注:233 答案:3 悬赏:0解决时间 2021-02-23 08:46已解决2021-02-22 22:32matlab中数据类型与c语言数据类型的区别?最佳答案2021-02-22 22:42简单谈一下。1.好多类型是对应的。输入一个数,ma…

oracle数据库安装提示M,Python第13课:oracle数据库的安装

Python第13课:oracle数据库的安装时间 2019-01-22下午4:30主讲 罗恒丰地点 四楼电教室版本:11.2大小:2.06G安装文件在专班ftp里python安装文件的文件夹。一.找到 stage/cvu/cvu_prereq.xml ,编辑系统名,以适…

linux 文件怎么不让删,请问如何设置权限,可以禁止用户删除文件

原帖由 WHITLACK 于 2009-9-28 08:48 发表 针对某个文件,如何设置权限,可以禁止删除?文件权限的r-w-x好像不能禁止删除的啊,谢谢指教!1:使用粘滞位可以做到,下面是介绍.强制位与冒险位、粘滞位针对u,g&…

linux运行中望cad,国产CAD软件中望的Linux版适配UOS, 我在国产系统里试了试

可能大家都知道,以往我们在国产操作里运行的软件,很多都是国外开源的软件。在以前很长一段时间里,国产操作中,国内企业很少去适配的。据说,国产深度之所以有很多国产软件,其中一个原因就是他们一家一家去拜…

linux内核死锁检测机制 | oenhan,Linux内核CPU负载均衡机制 | OenHan

还是神奇的进程调度问题引发的,参看Linux进程组调度机制分析,组调度机制是看清楚了,发现在重启过程中,很多内核调用栈阻塞在了double_rq_lock函数上,而double_rq_lock则是load_balance触发的,怀疑当时的核间…

linux下IPROTO_TCP,TCP/IP协议栈在Linux内核中的运行时序分析

可选题目三:TCP/IP协议栈在Linux内核中的运行时序分析在深入理解Linux内核任务调度(中断处理、softirg、tasklet、wq、内核线程等)机制的基础上,分析梳理send和recv过程中TCP/IP协议栈相关的运行任务实体及相互协作的时序分析。编译、部署、运行、测评、…

vs2019Linux守护,Visual Studio 2019将支援Ninja显着提升Linux专案建置效率

微软更新Visual Studio 2019,新增多个可提升Linux开发体验的功能,包括在Linux上支援建置系统Ninja,以及更完整地支援gdbserver,而且现在开发者也可以使用连接管理器(Connection Manager),编辑和配置预设的远端连接。使…

c语言程序中必不可少的,C语言程序设计(第3章程序控制语句)2

3.2 数据的输入与输出在程序的运行过程中,往往需要由用户输入一些数据,而程序运算所得到的计算结果等又需要输出给用户,由此实现人与计算机之间的交互,所以在程序设计中,输入输出语句是一类必不可少的重要语句&#xf…

android 九宫格封装,Android 九宫格布局

演示image需求满足0-9个图的适配图数量演示1image2image3image4image5image6image7image8image9image使用手动设置android:layout_width"match_parent"android:layout_height"wrap_content"app:ngl_gridSpace"10dp"app:ngl_oneChildHeight"…

android放大镜无广告,Android放大镜的实现代码

快三个月了没写博客了,因为工作调动,很多经验、心得都没有时间记录下来。现在时间稍微充裕了点,我会尽量抽时间将之前想写而没写的东西补上。进入正题。去年某个时候,我偶然看到一篇文章,讲android里面放大镜的实现。文…

鸿蒙和宙斯谁厉害,漫威宇宙宙斯vs奥丁,到底谁更强

宙斯在漫威里,是希腊神话中的众神之王,奥林匹斯十二主神之一,也是奥林匹斯大部分神和神奇女侠戴安娜的父亲,同时也是沙赞的力量来源之一能力:不朽(只有宙斯的血能杀死宙斯)宙斯神力雷霆之怒控制天气宙斯的力量并不是某…

背计算机专业英语词汇,计算机专业英语词汇1500词(五)

201. exit n. & vi. 出口;退出202. report vt. & n. 报告,报表203. execution n. 执行204. backup n. 备份,后备,后援205. version n. 版本206. find v. 寻找,发现207. pointer n. 指针,指示字208.…

优考试在线考试系统计算机,使用优考试在线考试系统解决企业员工考核评比

随着信息时代的高速发展,很多实体传统的东西已经慢慢搬到网络上了,在线考试就是在其中发展的很迅速的一种,企业的员工考核、员工的培训、评比都可以使用企业在线考试系统来解决了,电脑微信小程序手机考试相结合,让考试…

计算机专业申请phd美国,美国计算机专业博士的申请个人陈述范文

美国计算机专业博士的申请个人陈述范文2020-08-24 618人阅读摘要:美国计算机专业博士的申请个人陈述范文美国计算机专业博士申请个人陈述范文共享,公文个人陈述是美国博士申请公文中非常关键的构成部分,针对计划申请办理美国计算机专业博士研究生的同学们…

计算机桌面壁纸怎样拉伸,win10桌面壁纸怎么拉伸?手把手教你拉伸win10桌面壁纸的方法...

现在大家喜欢用自己拍摄的照片做电脑桌面壁纸,设置好win10桌面壁纸后,总感觉壁纸很显大,想要重新拉伸一下。那么win10桌面壁纸如何拉伸?针对此疑问,小编手把手教你拉伸win10桌面壁纸的方法。1、首先来看一下原因&#…

html+dom+深入,DOM 深入学习 - 1

本文章记录本人在深入学习Javascirpt DOM中看书理解到的一些东西,加深记忆和并且整理记录下来,方便之后的复习。DOM 版本w3c 指定的DOM规范包括多个版本,不同的版本(或称知为级别)又包含不同的子规法和模块,不同浏览器对DOM的支持…

计算机应用常用的30个函数,Excel中常用函数的使用

ISSN1009—30“咖船rKno别b内eand伯叻肋叻电奠知识…

cam350怎么看顶层_厉害的人是怎么分析问题的?(实操干货)

“经常做一个方案,几十页PPT还没把问题讲清楚,老板一个问题就貌似发现了关键,这到底是一种怎么样的思维方式?”我在职场这么多年,也遇到过很多次这样的问题。在我初入职场时,经领导点拨后,也产生…

抓球球的机器人应该怎么玩_王者荣耀:在游戏中当自己优势队友劣势的时候应该怎么玩?...

游戏里时常会有这么一种情况出现,我们疯狂Carry,一看战绩十几杠几,C得不行,但是队伍就是要输,颓势总是无法挽回。今天我就给大家说一下,是什么样的原因导致这种情况的发生。队友劣势的原因分析:…