不被抛出的异常不是异常
这个标题看起来难免有些奇怪,不过在PHP中,确实存在这样的问题。当你只是简单的将可能出现异常的代码用try catch包起来,妄图接住Exception的时候,往往得不到想要的效果。接下来,我们通过几段代码,来看一下问题的本质。
不能被捕获的异常
try {
$file = fopen('a.txt', 'r');
fread($file, 10);
fclose($file);
} catch (Exception $e) {
var_dump($e);
}
当a.txt不存在时,以上代码不会抛出错误并且走到catch里面,而是如下3个warning:
PHP Warning: fopen(a.txt): failed to open stream: No such file or directory in /Users/roychen/Dev/php-at/test.php
PHP Warning: fread() expects parameter 1 to be resource, boolean given in /Users/roychen/Dev/php-at/test.php
PHP Warning: fclose() expects parameter 1 to be resource, boolean given in /Users/roychen/Dev/php-at/test.php
正确的抛出姿势
try {
$file = @fopen('a.txt', 'r');
if (!$file) {
throw new Exception('Can not open file');
}
fread($file, 10);
fclose($file);
} catch (Exception $e) {
var_dump($e);
}
原理:用“@”抑制住可能发生的异常(这里指文件不存在),然后通过函数的返回(如果有异常则返回false)判断是否需要抛出异常。如此一来,异常就能被catch捕获了。
@ 符号简单介绍
@ 符号是PHP中比较特别(至少Javascript中没有)的一个符号,我认为是一个语法糖。主要作用就是使得@符号后面的语句不会抛出异常,反而使用Boolean返回值表示执行结果。常用情况:
- 打日志的时候
- 次要逻辑,不影响主流程。比方,用户注册完账户,发送感谢邮件。
最佳实践
上面我们讲了,如何在代码中跑出异常、在什么情况下使用@符号,但如果一直需要在业务层面解决异常的处理问题,始终难以避免异常的发生。接下来,带来一种方法,我认为他可能是php关于异常处理的最佳实践:set_error_handler。
set_error_handler
关于此函数的官方解释,请看这里。接下来,我们在全局设置一个error_handler。
function my_error_handler($errno, $errstr, $errfile, $errline) {
// 写日志
// 发送告警
}
set_error_handler('my_error_handler');
这是,再来看看上面一段文件写入的代码应该怎么写了?
function my_error_handler($errno, $errstr, $errfile, $errline) {
// 写日志
// 发送告警
}
set_error_handler('my_error_handler');
$file = fopen('a.txt', 'r');
fread($file, 10);
fclose($file);
#####my_error_handler会统一处理告警和错误日志记录的工作,当然,你如果希望在逻辑种特殊处理,那么也可以通过判断$file来自行添加处理逻辑。