江邊望海的技術人生
分享真知
如何组织PHP中的异常

江边望海语:作为一名WEB开发工程师,用PHP编写业务也有10年左右的历史了。现在对PHP的元问题特别的感兴趣。在进行代码Review阶段,我感到疑惑了,团队中有的开发者在程序中会抛出异常,有的则会输出返回码,有的甚至根本不做任何处理。那么,在编写代码的过程中要不要抛出异常?异常对于开发者有什么用?我们应该把异常放在代码中的哪些地方?我们应该怎么看待生产和收集生产环境的异常呢?

在看一些国外开发者的源码时,特别有感触,代码的严谨性使得阅读者感为观止,有种沐浴春风的感觉。但是,看一般开发者的代码就比较累,完全是一个人一个写法,百花齐放,没有一点儿严谨性可言。虽然这些拙劣的开发者写的的程序也能跑通,却为今后的项目重构贡献了不少自己的“力量”。

在学习异常的时候我们需要先理解一个基本概念:异常VS错误

PHP中的异常和错误是不同的,错误英文单词是 Error,异常英文单词是 Exception

异常一般指出现正常逻辑之外的情况,而错误是指运行时发生了不可恢复的故障,比如使用了未定义的变量,或者语法错误等。异常我们通过throw抛出,catch进行捕获,而错误一般发生程序就会终止,我们可以通过trigger_error触发用户级错误,然后通过set_error_handler设置处理函数。

为什么要使用异常?

先看一个没有使用异常的代码片段

function foo($input) {
    if ($input['user_id'] < 0){
        return -1; // 参数错误
    }
    // something else
}

上面的程序遇到错误时返回一个错误码,使用这种方式的好处是:我们每次在调用完函数后,都会检查返回值,当出现错误的时候,马上进行处理。

但是坏处也很明显:错误的处理和正常的业务逻辑耦合在了一起,我们平时开发中一个很恼人的感触就是:写一个业务逻辑,可能异常错误处理就占了2/3的代码,于是为了提高效率有人就发明了异常。

在php中对错误的处理有两种,一种是error和warnings,另一种是异常。

使用异常的代码片段:

try {
  find_slash($string);
} catch(Exception e) {
    //Handle exception
}

这样做的好处是:程序逻辑和错误处理分离了。你可以看到函数是如何工作的,同时也可以看到失败时候是怎么处理的。另外,现在可以提供更多的异常发生的上下信息,帮助你从发生的异常中恢复出来。

举个例子:当从数据库中获取一条记录的时候发生了异常,我们可以根据异常的不同类型,采取不同的结果。如果异常时由于没有我们想要的id记录,我们可能返回一个NullObject 是更好的方式,但如果异常是由于数据库连接的断开,我们可能会继续抛出异常,让异常被更上层的函数看到,因为这个异常在此处我们已经没有能够恢复的方法了。

相对于错误码一个异常对象, 可以包含更丰富的错误信息, 比如错误信息, 错误码, 错误的行数, 文件, 甚至出错上下文, 等等, 避免的”1.错误信息不丰富”的不足。

经验分享
  • 为了安全起见,线上环境最好不要用 try{}catch{}
  • 现在的很多框架的设计都是有入口文件, try{}cache{} 通常会在 index.php 文件中,相当于在最顶层加入了抛异常的做法。这样做的目的是只要整个程序出现任何错误,都会跳到异常处理的代码块去,很好的解决了代码流程控制的问题。
  • 异常本质上是程序执行流程的跳转,它最大的好处是可以将处理正常情况的代码和处理错误的分开,使代码可读性提高。