开发者学堂课程【PHP 进阶教程-由浅入深掌握面向对象开发-第三阶段:PHP 异常机制】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/713/detail/12740
PHP 异常机制
内容介绍:
一、介绍
二、步骤
三、小结
一、介绍:
1、目标:
了解异常机制,掌握异常的实现模式
2、概念:
异常机制:Exception,是面向对象中一种错误捕捉机制,允许我们能够自己捕捉错误。
(1)用来捕捉一些执行层面的问题(语法问题会在编译时就出现,开发可以避免)
执行层面可能要与用户交互出现错误数据导致出现问题,此时就需要去捕捉,因为开发难以避免。
(2)通常是因为一些不确定的内容导致代码无法正常运行,然后我们可以按照设定的逻辑来处理这些问题(例如要进行一个除法运算,用户提供除数为0,就属于执行层面的问题)
(3)异常机制允许开发者主动处理错误,而不是被动的处理错误(本身存在逻辑漏洞时需要主动捕捉)
(4)异常机制是面向对象语言中一种程度的处理错误的方式
(5)PHP提供了一套成熟的异常处理机制:Exception类
exception类是所有异常的基类,是一种架构。exception类中有许多信息为protected,还有final,说明可以继承这些类但不能重写即只可以扩展不能修改原始数据。
n PHP默认的是警告模式:即出错后立即报错给用户
n PHP想要使用异常机制:需要触发异常机制(set_error_handler(回调函数))相当于修改系统内部的错误处理方式变成自己控制。
(6)异常处理语法
n 捕获异常
try{
#可能出现异常的代码
}catch(Exception异常对象){
#主动处理异常(错误信息在异常对象中)
}
(对象中包含丰富的错误信息以及对应的处理方式、文件、行号、处理编号等。不会出错的代码不能放置在try中,因为try意味着会捕捉,效率会降低,因为在监督代码)
n 主动抛出异常
(有时存在错误没有错误,用户也没有错误,但是检查时发现用户没有错误但是业务无法进行,就可以主动抛出异常)
#根据逻辑判定主动终止代码执行
throw new Exception();
#不同的异常可以有不同的类
试想如果该抛出的对象在try中抛出:
try{
#可能出现异常的代码
throw new Exception();
}catch(Exception异常对象){
#主动处理异常(错误信息在异常对象中)
}
就会被捕捉到然后执行#主动处理异常(错误信息在异常对象中)。所以try{#可能出现异常的代码throw new Exception(); }属于在现场的管控,catch(Exception异常对象){#主动处理异常(错误信息在异常对象中)}属于管理者。发现异常会自动进入处理,后续代码都不会执行。
n 处理异常:在catch中主动处理异常,优雅的给用户提示结果
二、步骤
1、确定使用异常进行错误处理:设置异常处理解决方案
2、确定异常可能出现的种类,明确要使用的异常类(例如RuntimeException类,继承Exception,本身没有任何改变。异常处理所有,Runtime处理运行时的异常,存在结构对应,是一种架构。)
3、明确有可能出现错误的代码:使用try将代码块包裹
必要时在里面主动抛出异常
4、捕获异常处理异常,在catch中获取抛出的异常对象(捕获的异常一定为try中可以触发的异常否则捕捉不到。try与catch需要匹配上才能实现异常的监督和捕捉)
在catch内部进行错误处理
三、示例
1、明确可能出现错误的代码。
如:
$res = 4 / 0;
演示:创建文件38exception.php
编写代码:
<?php
#异常机制
$计算的数据来源于用户
$res = 4/0;
访问网址38exection.php结果显示:
Warning: Division by zero in D:\server\Web\38exception.php on line 7
上述代码在编译时不存在问题,但是在运行的时候因为被除数为0,所以会出现错误。而这个时候默认的,就是系统无法正确执行,立马给出错误(PHP默认的规则)。这种错误直接给出的方式不属于异常捕捉,异常捕捉需要用到异常类Exception类进行捕捉:使用throw
#外部传入数据:不确定
$n1 = 10;
$n2 = 5;
#要求$n1 / $n2
#外部传入数据:不确定
$n1 = 10;
$n2 = 5;
#要求$n1 / $n2
if($n2 == 0){
#被除数为0,不能操作:抛出异常
throw new Exception('被除数不能为0! ');
}
#没有问题继续执行
$res = $n1 / $n2 ;
演示:
将代码复制粘贴替换代码$res = 4/0;
刷新页面结果无内容,没有报错,因为抛出了一个异常代码就不会向下处理,实际上没有终止,是因为为0运行到了if($n2 == 0)中,若不为0则往下执行。
再来输入var_dump($res);
刷新页面结果显示
int(2)
是因为代码中$n2 = 5;若为0:$n2 = 0;
刷新页面结果显示报错:
数据是主动定义的,但是依然存在问题。没有编写系统处理的操作,系统只能抛出不做处理。
很明显,使用面向对象的方式会额外增加代码来实现错误处理。但是在面向对象中,异常机制通常很成熟,并不需要写额外很多的代码,而且绝大部分时间里,我们并不会去处理"不可能"出现问题的代码。
(目前只是将代码抛出但没有做处理,系统不能做捕获只能抛出)
2、可以将代码块放到一个会自动捕获错误的代码块中来实现异常的处理。异常捕捉语法如下:
#正常不会出错代码
echo 'hello world ' ;
$n1 = $_GET['n1'];
$n2 '= $_GET['n2 '];
#可能出现异常代码:使用try{}进行包裹捕捉
try{
#代码的执行具有未知性:但是代码没有语法错误
$res = $n1 / $n2;
#产生错误就会产生一个Exception类对象,捕获错误
}catch(Exception $e){
#捕获后的处理代码:如果try中没有问题,不会进入到catch内部
# $e中保存了$res = $n1 / $n2;会出现的错误
die('出错了...' );
}
演示:
注释掉代码:
#要求$n1 / $n2
if($n2 == 0){
#被除数为0,不能操作:抛出异常
throw new Exception('被除数不能为0! ');
}
#没有问题继续执行
$res = $n1 / $n2 ;
var_dump($res);
以上并没有实现异常处理,只是抛出了异常错误。
复制粘贴代码:
#可能出现异常代码:使用try{}进行包裹捕捉
try{
#代码的执行具有未知性:但是代码没有语法错误
$res = $n1 / $n2;
#产生错误就会产生一个Exception类对象,捕获错误
}catch(Exception $e){
#捕获后的处理代码:如果try中没有问题,不会进入到catch内部
# $e中保存了$res = $n1 / $n2;会出现的错误
die('出错了...' );
}
刷新页面结果显示:
Warning: Division by zero in D:\server\Web\38exception.php on line 27
为什么错误没有被捕获到呢?
3、以上代码在PHP中运行即便出错也不会执行,原因是系统默认使用的警告模式,即有任何问题都是直接报错如果要实现异常处理,必须借助于系统提供的set_error_handler(回调函数)来告知系统我们想采用的处理模式
set_error_handler(function (){
//直接编写一个匿名函数,匿名抛出一个错误
throw new Exception('错误! ');
});
#上述方式等价于先定义函数后注册
function exception_handler(){
throw new Exception( '错误');
}
set_error_handler( 'exception_handler ' );
//发现错误就扔出一个错误,然后告知系统放入exception_handler
使用set_error_handler进行真正异常处理,编写一个函数告知如果出现错误就进行抛错,真正产出对象。而上述演示写的代码不会产生对象,因为并没有告知系统出错时产生对象。所以以上演示代码也不可。
演示:
在上述代码上方添加:
#先需要告知系统使用异常处理:需要开发者明确如何产生异常
set_error_handler(function (){
throw new Exception('错误! ');
});
发现错误然后进行抛出,代码$res = $n1 / $n2;即为发现错误,然后扔出一个异常,catch(Exception $e)进行抓获,扔出的异常一定要与抓获的异常一致,否则抓不到。
刷新页面结果显示: 出错了...
结果说明$res = $n1 / $n2;产生的错误最终调用了throw new Exception('错误! ');进行报错然后到catch(Exception $e)被抓获,被抓获后运行处理的代码die('出错了...' )。
以上就是异常的逻辑,需要系统抛出异常才能抓获。遇到错误throw new Exception('错误! '),发现错误系统进行报错,扔出一个对象$res = $n1 / $n2,catch(Exception $e)进行检测抓获,抓获后进行执行。如果$res = $n1 / $n2下还有代码:echo $res;也不会执行。刷新页面结果显示:出错了...
因为一旦异常出现:并且被捕获,那么try里面的代码就不再执行。但如果代码在外侧:在最终存在代码:echo ‘hello world’;刷新页面结果显示:出错了...
外侧代码并不是没有执行,因为在上述已经进行了die,如果没有die,只是进行echo:echo '出错了...' 就会继续执行外侧。刷新页面结果显示出错了...hello world
所以异常只控制try里面的代码,不能控制try catch之外的代码。
4、有了上述代码在错误处理之前,那么系统碰到错误,就会调用我们定义的function来进行错误处理:处理也分两种模式:有try和没有try
#接上述代码:没有try
$res = 5 /0;
#错误:直接报错,但是实际上已经调用了自定义错误处理函数,只是异常没有处理,系统处理了
#有try: try和catch不分家
try{
$res = 5 / 0;
}catch(Exception $e){
die('错误:被除数不能为0');
}
以上就说明了一个处理模式,可以自己控制好错误模式,可以优雅的处理。但是此时可以发现,做了上述操作,但没有使用$e处理任何东西。如果需要处理就需要了解对象所属的方法。
5、异常对象$e到目前为止没有产生任何作用,其实Exception类中有很多的方法和属性,可以帮助我们统一获取错误信息的
/*属性*/
protected string $message ;
protected int $code ;
protected string $file ;
protected int $line ;
/*方法*/
public _construct ([ string $message = "" [, int $code = 0 [,Throwable $previous = NULL ]]])
final public string getMessage ( void )
//获取错误描述信息
final public Throwable getPrevious(void)
final public int getCode ( void )
//获取错误编号
final public string getFile ( void)
//获取出现错误的文件:大项目很有用
final public int getLine ( void )
//获取出错的行
final public array getTrace ( void )
final public string getTraceAsString ( void )
public string _toString ( void )
//对象可以直接echo
具体使用如下
try{
#可能出现问题的代码块
}catch(Exception $e){
echo '代码运行错误! <br/> ';
echo '错误文件为: ’ . $e->getFile( ). '<br/> ';
echo '错误行号为: '. $e->getLine( ) . '<br/> ';
echo '错误描述为:’ . $e->getMessage();
die();
//如果想要优雅可以在此处实现一个跳转
}
演示:
复制代码echo '代码运行错误! <br/> ';
echo '错误文件为: ’ . $e->getFile( ). '<br/> ';
echo '错误行号为: '. $e->getLine( ) . '<br/> ';
echo '错误描述为:’ . $e->getMessage();
die();
代替上述演示中的代码echo ‘出错了...’
刷新页面结果显示:
报错代码为throw new Exception('错误! '),因为此处抛出了错误信息,而不是抓出了具体。因此我们要针对不同位置抛出不同异常才能有真正错误信息。但真正产生错误的位置应该为$res = $n1 / $n2。
6、有的时候代码如果走到了一个"死胡同”,即代码执行没有任何问题,但是不符合我们的逻辑。以前我们是跳转提示,现在可以抛出异常,交给异常来处理: throw new Exception();
try{
if($n != 0){
$res = 10 / $n;
}else{
#业务没法发展了,直接抛出异常
throw new Exception('被除数不能为0! ');
}
}catch(Exception $e){
echo "代码运行错误! <br/>';
echo '错误文件为: ' . $e->getFile() . '<br/> ';echo '错误行号为: ' . $e->getLine( . '<br/> ';echo '错误描述为:' . $e->getMessage(;
die( ;
}
演示:
在代码$res = $n1 / $n2;上添加:
if($n2 == 0)throw new Exception(‘被除数不能为0!’);
此时代码$res = $n1 / $n2;则不会执行,因为上面已经主动放出了错误,Exception会在下面的catch中被捕获,错误信息为被除数不能为0!
刷新页面结果显示:
以上是异常类的主动运用。
三、小结
1、异常是面向对象中处理错误的一种方式(但是是针对运行时的错误,语法错误无法检查),能够让整体用一种统一的方式对外处理错误(这种错误方式比较优雅,不会给用户报错一堆代码)
2、异常机制是利用try(catch(Exception $e))来进行捕捉和处理的(需要根据捕获的类来使用Exception,不同的错误使用不同类来抓获),Exception类提供了很多方法可以方便获取各种错误相关信息(其它类都是继承Exception,方法一致,都不可重写)
3、PHP默认是警告模式(还有静默模式和异常模式),即直接系统给出错误,如果想使用异常处理,那么就需要设置错误异常模式set_error_handler(异常回调函数)来实现
4、异常模式中,必须使用try{}catch(){}才会捕捉异常,否则只会系统提示(如果因为异常处理代码太麻烦,可以将异常处理代码封装成函数或者类来实现代码简化)