PHP 异常和错误处理:错误处理机制 Draft
处理原则
如果没有做任何配置,PHP 的错误是会直接打印出来的。古老的 PHP 应用也确实有这么做的,但现代应用显然不能这样,现代应用的错误应该遵循以下规则:
一定要让 PHP 报告错误;
在开发环境中要显示错误;
在生产环境中不能显示错误;
在开发和生产环境中都要记录错误。
生产环境下的处理机制
在生产环境下,错误不能直接打印出来,应该记到 log 文件中,并返回给用户一个笼统的错误信息。set_error_handler 函数 就是设置用户自定义的错误处理函数,以处理脚本中出现的错误。我们可以在这个函数中将错误信息打到 log 文件中,并统一返回错误信息。
本来这个函数是搭配 trigger_error 函数 使用的。用户通过 trigger_error
产生 error,然后用 error_handler
来处理错误。只是在这种场景下往往「异常」更好用,所以这么用的并不多。
值得注意的点
-
在 错误级别 所述的 16 种错误中,有一部分相当重要的错误并不能被 error_handler 捕获:
以下级别的错误不能由用户定义的函数来处理: E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、E_COMPILE_WARNING,和在调用 set_error_handler() 函数所在文件中产生的大多数 E_STRICT。
-
error_handler 处理完毕,脚本将会继续执行发生错误的后一行。在某些情况下,你可能希望遇到某些错误可以中断脚本的执行。
也就是说,我们处理完 E_WARNING 之后,需要及时退出脚本( 通过 die() 或 exit())。
PHP7 的错误
PHP 7 改变了大多数错误的报告方式。不同于传统(PHP 5)的错误报告机制,现在大多数错误被作为 Error 异常抛出(在 PHP7 中,只有 fatal error 和 recoverable error 抛出异常,其他 error 比如 warning 和 notice 的表现不变)。PHP7 中的 Error 和 Exception 的关系如下:
interface Throwable
|- Exception implements Throwable
|- ...
|- Error implements Throwable
|- TypeError extends Error
|- ParseError extends Error
|- ArithmeticError extends Error
|- DivisionByZeroError extends ArithmeticError
|- AssertionError extends Error
值得注意的是,Error 类表现上和 Exception
基本一致,可以像异常 Exception
一样被第一个匹配的 try / catch
块所捕获,如果没有匹配的 catch
块,则调用异常处理函数(事先通过 set_exception_handler() 注册)进行处理。
如果尚未注册异常处理函数,则按照传统方式处理,被报告为一个致命错误(Fatal Error)。但并非继承自 Exception
类(要考虑到和 PHP5 的兼容性),所以不能用 catch (Exception $e) { ... }
来捕获,而需要使用 catch (Error $e) { ... }
,当然,也可以使用 set_exception_handler
来捕获。
但是,用户不能自己定义类实现 Throwable
,这是为了保证只有 Exception
和 Error
才可以抛出。
PHP7 的 ERROR 处理
PHP7 中的 fatal error 会抛出 Error,且可以被正常 catch
到:
<?php
$a = 1;
try {
$a->nonexist();
} catch (Error $e) {
// Handle error
}
也有些错误场景下会抛出更加详细的错误,比如:
<?php
// TypeError
function test(int $i) {
echo $i;
}
try {
test('test');
} catch (TypeError $e) {
// Handle error
}
// ParseError
try{
eval('i=1;');
} catch (ParseError $e) {
echo $e->getMessage(), "\n";
}
// ArithmeticError
try {
$value = 1 << -1;
} catch (ArithmeticError $e) {
echo $e->getMessage(), "\n";
}
// DivisionByZeroError
try {
$value = 1 % 0;
} catch (DivisionByZeroError $e) {
echo $e->getMessage(), "\n";
}
结语
PHP 7 完善了 PHP 5 鸡肋的错误处理机制,可以说是一个很大的进步。