2018年四月月 发布的文章

说说php的try-catch那些事情

写在前面

  首先,区别 ErrorException 两个概念,这是两个不同的东西。错误是不可忽视的,异常/高级别的错误通常会引起程序停止,而低级别的错误如notice等是可选择忽视的,一般不会造成程序停止运行。当然,个人认为,在不区分语境的情况下,有时出错和出问题可以认为是同一个意思,尽管这两者产生的影响程度上有很大的差别。当我们说到抑错机制的时候,很自然的联想到try-catch接下来说的内容以try-catch的某些方面通过问题的形式展开。

为什么要使用try-catch ?

try-catch 的加入会减少大量 的代码噪音。如何理解噪音?不必要的代码,抑错过度使用。php5-里面因为机制的不完善,须得使用不少的判断语句来减少Error 和 Exception的产生,以便于程序能最大程度的执行到底,诸如 isset(),empty(),is_file(),file_exists.....等等。但是php5里,个人并不建议使用try-catch,至于评价,食之无味,弃之可惜

php的try-catch效果如何?

为什么try-catch强大,评价却褒贬不一,我想这主要还是分版本。
- 对于php5,如果非要我坦诚,就是两个字,呵呵。说的难听一点,学java学的不到位,高不成低不就,可利用价值不高。因为php5的抑错能力非常有限,Exception必须手动throw才能正常的catch,而对于错误,并没有捕捉的能力。这一点和java、javascript这些语言不同,异常不会自动抛出,自然也不会进入到catch里面。如果定义了set_exception_handler (php5+支持),则可以利用此函数进行所有未捕捉的Exception的处理,但是这也仅仅局限于手动throw的异常,总之,对于错误无可奈何。而在大多数情形下,throw 总是可用return die/exit规避,注意规避并不代表能完全代替;有人会说用set_error_handler,是没错,不过这和try-catch并没有任何关系,而且对于Fatel Error,依然没有任何效果
- 对于php7,Throwable的加入,使得Error类或Error类的派生类的错误对象产生的大部分Fatel Error都可以像Exception一样正常的被捕获(try-catch/set_error_handler),从这里开始,抑错机制才真正的逐渐完善起来。

演示一个小demo?

假设存在以下代码,在php-5.6.27环境下运行

set_exception_handler(function () {
echo "产生了一个异常";
});
set_error_handler(function () {
echo "产生了一个错误";
});
echo $php['test'];
throw new Exception("test", 1);
php();

运行的结果:产生了一个错误 产生了一个异常
这时也许某些人会有疑问,php()不存在为什么不报错?前面就说过了,通常异常会引起程序停止,所以php()在之前已经被短路了。

注释掉throw所在行呢?

运行结果: 产生了一个错误 Fatal error: Call to undefined function php()
并不会捕捉Fatal Error.

那么try-catch php()呢

不用想肯定也是这样 Fatal error: Call to undefined function php()

下面是在7.0.12下运行的

try {
    echo $php['test'];
    php(); //未定义的函数
} catch (Throwable $e) {
    echo '捕获';
}

运行结果:PHP Notice: Undefined variable: php 捕获

如何合理的运用try-catch或者我需要使用try-catch吗?

当你觉得return/die 能够解决的问题的时候,那就没有必要。当你需要一个健壮的代码层,以便于上级逻辑能够调用并且针对性的处理,比如写一个延展性非常好的mysqli类,或者一个接口的包括代码、错误信息等json,而并非在产生错误时立即停止所有程序,包括调用本层的上级逻辑
也就是说,产生错误的时候,只结束当前层,不结束上层逻辑并抛出信息等待处理,那么try-catch也许是个不错的思路。当然,上面的最后一个例子中,php7环境下还是不会捕捉可忽略的错误,所以,适当的使用set_error_handler和set_exception_handler加持,也许是个非常好的想法。php5里面,如果要使用try-catch,其实要做throw的判断工作和直接return,差不了太多。只是在上层逻辑里面。php是使用is_null / empty/isset等判断还是使用try-catch来加持,这是个值得思考的问题。php7里面,可以考虑直接上重武器了。

减少if-else的滥用,提高代码可读性

背景

我遇见不少人都是这样,多层逻辑嵌套,看似条例清晰、逻辑关系明了。实际上读起来真的很费力。看个最简单的例子,一吧友A想要发一个帖子宣传xx产品,需要征求所有吧主(bz1,bz2)一致同意后才可以发帖。

方案 

  有人可能会这样写:

function getPostAgree(){
    $agree = false; //flag
    if ($bz1 == "agree){
        $agree = true;
        if ($bz == "agreem){
            $agree = true;
        }else{
            $agree = false;
        }
    } else {
        $agree = false;
    }
 return $agree;
}

  看上去很自然. 逻辑也很清楚,bz1同意了之后bz2再同意肯定就没啥问题了.
  有人会这样写:

function getPostAgree(){
    $agree == false; //flag
    if($bz1 == 'agree' && $bz2 == "agree"){
        $agree = true;
    }else{
        $agree = false;
    }
    return $agree;
}

  这样看上去逻辑更加清楚了,两个吧主同时同意.
  还有第三种 第四种:

function getPostAgree(){
    $agree == false; //flag
    if($bz1 == 'disagree'){
        return $agree;
    }
    if($bz2 == 'disagree'){
        return $agree;
    }
    return $agree=true;
}
function getPostAgree(){
    $agree == false; //flag
    $bz = [$bz1,$bz2];
    if(!in_array('disagree',$bz)){
     $agree=true;
}
return $agree;

  以上看上去都行得通,那么现在又多个1个吧主呢?10个呢?50个呢?
第一个嵌套50个if - else
第二个写49个&&
第三个写50个if
第四个,变成50的数组。而且,同时可以分离出所有bz

$bz = [$bz1,$bz2];
function getPostAgree()
{
    global $bz;
    $agree = false; //flag
    $agree == !in_array("disagree", $bz);
    return $agree;
}

如何改进?

  • 嵌套逻辑用平行逻辑代替
  • 使用表驱动减少判断
  • 不是必要的if-else,能不else就别else,不要if-elseif-elseif...-elseif-else
  • if-return 代替 if-else
    .....