讨论php中异常的使用

由于PHP对于异常的支持并没有JAVA等语言那么全面,导致很多初学者在写代码的过程中干脆不使用异常,很多情况下,这样做也不会产生什么问题,但在一些必要的场景下,这样做一方面程序上面会有瑕疵,另一方面,也暴露了自己菜鸟的特征,为了向高手靠近,我们有必要弄明白一下异常是个什么东东以及怎么使用异常这两个问题。

异常是什么?

未答问题,先上代码!

1
2
<?php
throw new Exception('Hello,Exception');

刷新浏览器,我们看到,一个丑陋的带着致命错误的页面出现在我们面前,页面内容如下:
Fatal error: Uncaught exception 'Exception' with message 'Hello,Exception' in /www/test/exception2.php:2 Stack trace: #0 {main} thrown in /www/test/exception2.php on line 2
通过以上的hello,exception,我们可以知道两点:

  1. PHP默认并不对异常进行捕获,需要程序员手动捕获异常并对其进行处理;
  2. PHP对于没有进行捕获的异常默认使用Fatal error(致命错误)来处理。
    对于一个对代码有极强控制欲的IT民工来说,发生如上事件是不可接受的,好吧,既然是出现了致命错误,那我就把致命错误关闭了,看看你会怎样?
    1
    2
    3
    <?php
    ini_set('display_errors', false);
    throw new Exception('Hello,Exception');

我们在程序开始的时候把错误给屏蔽了,刷一下页面:
500错误页面
我靠!这是什么鬼!比刚才那个页面还要丑,提示我服务器500错误!
好吧,现在我们知道了,对于抛出的异常,必须进行捕获。好吧,来捕获一下吧!

1
2
3
4
5
6
7
<?php
ini_set('display_errors', false);
try{
throw new Exception('Hello,Exception');
}catch(Exception $e){
}

再次刷新页面,OK!一个干净的页面出现了,没错,什么也没有就对了。上面的代码中,我们用try语句,把异常进行了捕获,由于try语句后面必须跟上catch语句块,所以后面的catch语句块是不可省的,然后,就没有然后了,因为我们没有对捕获到的异常进行处理,所以,页面就什么都不显示。正常的开发中,肯定不能什么都不处理,那相当于你在大街上看见小偷把你妈妈的钱偷走了,而你却假装没看见一个样,所以,接下来,我们来处理一下刚才捕获的异常。

1
2
3
4
5
6
7
<?php
ini_set('display_errors', false);
try{
throw new Exception('Hello,Exception');
}catch(Exception $e){
echo '妈妈,有小偷!';
}

嗯,很好,刷一下页面,没错,现在就对了,看到小偷偷妈妈的钱要大胆的说出来!

异常处理句柄函数

如果程序中的每个异常都要亲自去处理,码农的命也太苦了!更重要的是,代码中到处都是try……catch……的异常捕获块,爱美的我受不鸟啊!幸运的是,我们有set_exception_handler函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
ini_set('display_errors', false);
set_exception_handler('handleException');
throw new Exception('Hello,Exception');
/**
* 自定义异常处理句柄函数
* */
function handleException($exception){
if ($exception instanceof ExitException) {
return;
}
echo '<h1>妈妈,有小偷</h1>';
}

现在我们有了健全的“自动抓小偷机制”,但是,对于我们自己写的代码,应该在哪些地方抛出异常来让我们的自动捕获机制捕获这些异常呢?

异常中断特性

1
2
3
4
5
6
7
8
<?php
ini_set('display_errors', false);
set_exception_handler('handleException');
echo '没有异常的时候,小熊快乐的生活'."<br>";
echo '没有异常的时候,小羊快乐的生活'."<br>";
echo '没有异常的时候,小呗快乐的生活'."<br>";
echo '没有异常的时候,小青快乐的生活'."<br>";
echo '没有异常的时候,小年快乐的生活'."<br>";

在没有任何异常的情况下,以上代码,塑造了一个平和的世界。大家都在快乐的生活。现在我们引入异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
ini_set('display_errors', false);
set_exception_handler('handleException');
echo '没有异常的时候,小熊快乐的生活'."<br>";
echo '没有异常的时候,小羊快乐的生活'."<br>";
echo '没有异常的时候,小呗快乐的生活'."<br>";
throw new Exception('Hello,Exception');
echo '没有异常的时候,小青快乐的生活'."<br>";
echo '没有异常的时候,小年快乐的生活'."<br>";
/**
* 自定义异常处理句柄函数
* */
function handleException($exception){
if ($exception instanceof ExitException) {
return;
}
echo '<h1>'.$exception->getMessage().'</h1>';
}

查看结果:
exception2
我们发现,默认情况下,出现异常,会中断当前执行的程序,而且后续代码也不执行了,为了避免这种情况,我们可以稍微优化一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
ini_set('display_errors', false);
set_exception_handler('handleException');
try{
echo '没有异常的时候,小熊快乐的生活'."<br>";
echo '没有异常的时候,小羊快乐的生活'."<br>";
throw new Exception('Hello,Exception');
echo '没有异常的时候,小呗快乐的生活'."<br>";
}catch(Exception $e){
echo 123;
echo "<hr>";
}
echo '没有异常的时候,小青快乐的生活'."<br>";
echo '没有异常的时候,小年快乐的生活'."<br>";
/**
* 自定义异常处理句柄函数
* */
function handleException($exception){
if ($exception instanceof ExitException) {
return;
}
echo '<h1>'.$exception->getMessage().'</h1>';
}

查看输出:
exception3
我们可以看出,对于自己写的可能发生异常的代码,我们用try……catch……包起来,如果在catch语句块我们处理了异常,php会使用我们处理异常的逻辑,并且处理完之后,继续执行catch代码块之后的代码,如果不处理呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
ini_set('display_errors', false);
set_exception_handler('handleException');
try{
echo '没有异常的时候,小熊快乐的生活'."<br>";
echo '没有异常的时候,小羊快乐的生活'."<br>";
throw new Exception('Hello,Exception');
echo '没有异常的时候,小呗快乐的生活'."<br>";
}catch(Exception $e){
echo 123;
echo "<hr>";
throw new Exception('还给你系统自己处理去');
}
echo '没有异常的时候,小青快乐的生活'."<br>";
echo '没有异常的时候,小年快乐的生活'."<br>";
/**
* 自定义异常处理句柄函数
* */
function handleException($exception){
if ($exception instanceof ExitException) {
return;
}
echo '<h1>'.$exception->getMessage().'</h1>';
}

查看输出:
exception4
我们发现,如果我们不手动处理异常,那么,程序就会调用我们的异常处理句柄函数来处理异常,并且程序终止于发生异常的地方,而如果我们对异常进行了处理,那么,程序将使用我们对异常处理的逻辑处理异常,并且继续往下执行程序。但是不管怎么样,try{语句块}语句块里面,异常发生之后的代码将再也没有机会执行。

异常的应用场景

终于到激动人心的时候了,通过上面对异常特点的研究,我们可以知道:
1、异常可以由程序员手动抛出;
2、程序员可以捕获异常,并对异常进行自行处理,或者再次抛出。
3、如果异常在程序中没有被处理,将会层层外抛,最终被我们设置的异常处理句柄函数所处理(如果注册了的话,没注册就会报服务器500错误)。

应用1、优雅提示一些不可避免的系统错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
ini_set('display_errors', false);
set_exception_handler('handleException');
$extensions = get_loaded_extensions();
if(!in_array('redis',$extensions)){
throw new Exception('unsurport redis extension!');
}
$redis = new Redis();
if(!$ret = $redis -> connect('127.0.0.1',6379)){
throw new Exception('Can\'t connect to Redis');
}
var_dump($redis);
echo '<hr>';
$name = $redis->get('name');
if(!$name){
$name = 'ZhangSan';
$redis->set('name',$name);
}
var_dump($name);
function handleException($exception){
if ($exception instanceof ExitException) {
return;
}
echo '<h1>'.$exception->getMessage().'</h1>';
}

以上代码示例了利用抛出异常结合异常句柄函数美化类似网络连接出错等提示信息。对于这类一旦出错,程序没必要继续往下执行的异常,我们在主逻辑中只需要处理抛出异常即可。当然,对于生产环境,在抛出前,我们可以给管理员发短信提示一下。

场景2、底层封装处理抛异常的动作,外层逻辑捕获异常,获取错误提示信息

对于MVC框架的PHP应用程序,由于PHP的返回值的单值。很多时候底层核心模块的封装,我们不仅需要返回错误码,还需要把错误的提示信息也返回给最终的控制器来处理。(当然,其实也可以不处理,依赖场景1中注册的句柄函数处理)

场景3、与事务搭配使用

这里的事务包括但不限于数据库的事务,比如,一个常见的业务逻辑,用户上传一张图片,并把图片的地址记录到mysql中,这两件事也可以组成的事务,我们就可以配合异常机制,确保图片和记录同时被上传和被插入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
ini_set('display_errors', false);
set_exception_handler('handleException');
class uploadException extends Exception{
}
class insertException extends Exception{
}
try{
//如果上传图片失败,抛出上传图片异常
//如果插入记录失败,抛出插入记录失败异常
}catch(Exception $e){
//判断捕获的异常,
//如果是上传图片异常,那么检查数据库中是否有对应的记录,有则删除
//如果是插入数据库记录异常,查看图片是否上传成功,成功则把图片删除
}

看来写博客真的很不容易,收获也颇多,让自己思考了平时没注意到的许多问题并串成了线。希望自己能够坚持下去!加油!