面向对象 - 异常

  • 作者:KK

  • 发表日期:2017.7.30


要点速读

  1. 异常是一个类,这个类的类名通常叫Exception,或者夹带着这个词眼的其它类名

  2. 它是在程序出现意外的情况的时候进行抛出的,此时称为抛出异常,比如throw new Exception('程序出错!');这么一句代码就是抛出异常,关键词throw

  3. 一旦抛出异常,后面所有程序都会中断运行

  4. 在程序能判断控制处理的范围内不应该抛出异常,该返回false就返回false,实在是不能再往后运行了再考虑抛出异常吧

  5. 为什么要抛出异常而不用exit来中断后面的程序呢,原因就是异常会附带更多的运行信息,方便做日志

  6. 我们可以通过set_exception_handler函数设置一个回调,在程序发生异常时会触发这个回调,一般在这里会做日志记录


感受一下异常

echo 111;
throw new Exception('出错出错');
echo 222;

运行以上代码后大概的显示结果如下图:

先是执行了第一句代码输出了111,然后下面就是一串报错信息,脚本的第3行222并没有echo出来,简单地说就是被异常终止了

而你可以从此认识到,PHP的异常一旦出现了,那报错的开头一般都是显示为“Fatal error: Uncaught Exception: XXXX”,其中这里的XXXX就是new这个异常的时候传进去的报错字符串信息


抛出异常的关键在于throw

第2句代码其实相当于这样一个过程:

$exception = new Exception('出错出错'); //实例化一个Exception类
throw $exception;	//通过throw关键字将这个类抛出去,实现了抛出异常

throw关键字翻译成中文就是的意思,被抛出去的对象必须是一个实例,也就是一个被new出来的对象,这个要注意记一下,因为有时候很多菜鸟会错误地写成throw Exception('报错信息');(少了new,没有实例化Exception)

其实并不是所有类都可以被抛出的,比如这样是非法的:

class MyException{} //定义一个自己的类,这个类是空类

throw new MyException();

具体就不在这个入门学习的阶段介绍了,初级PHP工程师只要知道通常只有PHP自带的Exception会被抛出就好,这里也包含了继承的子类,看下面


异常可以被继承

class MyException extends Exception{
	public function __toString(){
		$error = '自定义的异常类:' . $this->getMessage();
		return $error;
	}
}


echo 111;
throw new MyException('出错出错');
echo 222;

只要继承了Exception,那自己也算是一个Exception了,所以可以被抛出

那么继承的好处在哪里呢?看上面的__toString方法,那可是能够自定义输出的异常信息呀,这样我们就可以显示一个漂亮的异常提示页面了,而不是枯燥的一段文字,用户浏览网页时如果看到这样的报错感觉更加不友好啦


还有另一个内置异常叫ErrorException

echo 111;
throw new ErrorException('出错出错');
echo 222;

看着跟Exception没什么区别是吧,简单地说抛出这个异常意味着程序出现了更严重的错误

没事,你现在也不用深入去纠结这个事,只要发现抛异常的时候你只要知道是Exception或ErrorException就好了


捕捉异常

其实异常只要出现了,不管你运行到了什么地方都会被停止运行,比方这样吧:

function test(){
	echo 'test111';
	throw new Exception('出错咯');
	echo 'test222';
}


echo 'aaa<br/>';
test();
echo 'bbb';

这个throw语句虽然在test函数里,但它就是抛出了异常,就是会引发整个程序停止,不仅是test内部停止了运行,外部也停止了运行

如果不想停止运行可以用try语句来进行捕捉异常

function test(){
	throw new Exception('出错咯');
	echo 'test';
}


echo 111;
try{
	test();
}catch(Exception $exception){
	echo '发生了异常,这个异常的报错消息是:' . $exception->getMessage();
}
echo 222;

这样程序不会被停止,try关键字的意思是:尝试运行一下花括号里面的代码,然后接着有个catch语句表示如果上面的尝试运行代码出现了异常,那我这里就看看它是不是一个Exception,如果是就保存到$exception这个变量里,并且交给我下面的花括号代码来处理事情

try...catch就是这样配合着做事的,做完后跳出catch然后正常地执行了后面的echo 222,于是整个程序就不会被停止

然而如果你把上面测试代码里test函数里面的throw语句屏蔽掉让它别抛出异常,那么catch语句也不会被执行,因为catch是捕捉的意思,根本没有异常,它埋伏失败,自然它花括号里的代码也没有相关的行动

可是还有一个注意点,如果把上面抛出异常的代码改成throw new ErrorException('这回抛的是另一个异常');之后,catch也不会被运行,因为catch指定了要捕捉Exception这个异常,然而test抛出的却是ErrorException,所以没有埋伏到、没有捕捉到它想要的目标,于是就没管了,结果没被捕捉到,这个ErrorException就传到了系统,系统收到了异常就会做这么一件事:停止所有程序的运行,所以echo 222也不会被运行,除非你同时改成catch(ErrorException $exception),自己试试


统一异常处理

当异常被抛出到系统级别的时候,我们其实可以事先埋伏一个最终捕捉函数,先试下代码:

set_exception_handler(function($exception){
	echo '<span style="color:red;">抱歉,网站出错了,请联系管理员,错误信息:' . $exception->getMessage() . '</span>';
});

echo 111;
throw new Exception('出错出错');
echo 222;

想像到一些网站的“网站出错,请联系管理员”是怎么弄的了吗?如果是PHP,这就是基点一个做法~

如果你作为新手入职一家公司,接手一个项目,不仿在这堆代码里搜索一下有没有set_exception_handler函数的调用,它设定了怎样的处理逻辑,这样能从前辈的代码里学到异常的处理思想哦