set_error_handler无法捕获fatal error,仅处理e_warning、e_notice等可恢复错误;e_error类致命错误需通过register_shutdown_function + error_get_last()捕获。

set_error_handler 无法捕获 Fatal Error
直接说结论:set_error_handler 函数根本收不到 E_ERROR(Fatal Error)这类错误。它只处理 E_WARNING、E_NOTICE、E_USER_* 等可恢复的错误,而 E_ERROR、E_PARSE、E_CORE_ERROR 会在脚本执行中断前就终止流程,连函数调用的机会都没有。
所以“在自定义错误处理里区分 Notice 和 FatalError”这个前提本身不成立——FatalError 压根进不了你的 set_error_handler 回调函数。
-
E_NOTICE:能进set_error_handler,$errno参数值为 8 -
E_ERROR:不能进,set_error_handler完全无感知 - 常见触发
E_ERROR的操作:调用未定义函数、实例化不存在的类、内存耗尽、new失败且无异常捕获等
如何实际区分并响应这两类错误
得用组合策略:靠 set_error_handler 拦 E_NOTICE,靠 register_shutdown_function 补漏 E_ERROR。
-
set_error_handler中检查$errno === E_NOTICE就是 Notice -
register_shutdown_function里调用error_get_last(),若返回非null且type是E_ERROR或E_PARSE,基本就是 Fatal Error - 注意:
error_get_last()在脚本正常退出时也返回null,必须先判断是否真有错误发生 - 不要在 shutdown 函数里再抛异常或触发新错误,否则可能掩盖原始致命错误
为什么不能只靠 error_reporting 控制显示
error_reporting 只决定“报不报”,不改变错误能否被捕获。比如设成 error_reporting(E_ALL & ~E_NOTICE),E_NOTICE 就不会输出,但它依然会进 set_error_handler(只要 handler 已注册);而 E_ERROR 即使开着 display_errors,你也拿不到控制权。
-
error_reporting(E_NOTICE):只让 Notice 显式上报,但不影响其可捕获性 -
ini_set('display_errors', 'Off'):只是隐藏输出,Fatal Error 仍会终止脚本 - 线上环境应关掉
display_errors,但保留log_errors和日志路径
一个轻量可用的区分示例
function handle_php_error($errno, $errstr, $errfile, $errline) {
if ($errno === E_NOTICE) {
error_log("[NOTICE] $errstr in $errfile:$errline");
return true; // 阻止默认处理
}
// 其他如 WARNING、USER_NOTICE 也可在这里分路径处理
}
set_error_handler('handle_php_error');
register_shutdown_function(function () {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR])) {
error_log("[FATAL] {$error['message']} in {$error['file']}:{$error['line']}");
// 这里可发告警、写监控指标,但别 echo 或 exit
}
});
关键点在于:Notice 是“主动通知”,Fatal 是“事后抢救”。两者机制完全不同,硬塞进同一个逻辑分支只会漏判或误判。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











