Fatal error无法被set_error_handler捕获,因其属PHP引擎硬限制;仅E_WARNING、E_NOTICE等可恢复错误可被捕获,致命错误需用register_shutdown_function+error_get_last()兜底。

Fatal error 根本不能被自定义错误处理器捕获,这不是配置或写法问题,而是 PHP 引擎层面的硬限制——set_error_handler 对 E_ERROR、E_PARSE 等致命错误完全无效。
为什么 set_error_handler 捕获不到 Fatal error
因为 set_error_handler 只处理「可恢复」的错误,比如 E_WARNING、E_NOTICE、E_USER_ERROR;而 Fatal error(如调用未定义函数、语法错误、类未找到)发生在 Zend 引擎解析或执行崩溃点,控制权从未交还给用户代码。哪怕你把它放在 index.php 第一行,只要后续某处 require 'missing.php' 出错,set_error_handler 的回调根本不会执行。
常见误判现象包括:
- 本地
error_reporting(E_ALL)有报错,线上却静默白屏——大概率是display_errors = Off且没配日志路径 - 写了
set_error_handler却收不到Call to undefined function日志,不是函数没注册,是它压根不触发 - 试图在 handler 里
throw new Exception()转成异常,结果脚本直接双崩(原始 fatal + 新 throw)
唯一可行的兜底机制:register_shutdown_function + error_get_last()
这是 PHP 中唯一能稳定介入 Fatal error 发生后的时机。它不恢复执行,但能记录原始错误、返回友好提示或触发告警。
立即学习“PHP免费学习笔记(深入)”;
必须满足三个条件才可靠:
- 放在入口文件最顶部(如
index.php第一行),否则中间require出 fatal 就来不及注册 - 只调用轻量操作:仅
error_get_last()判断 +error_log()写日志,禁止连数据库、发 HTTP 请求、调用对象方法 - 严格过滤类型:
in_array($error['type'], [E_ERROR, E_PARSE, E_COMPILE_ERROR, E_CORE_ERROR]),避免把普通 warning 当 fatal 处理
示例片段(直接可用):
register_shutdown_function(function () {
if ($error = error_get_last()) {
$type = $error['type'] ?? 0;
if (in_array($type, [E_ERROR, E_PARSE, E_COMPILE_ERROR, E_CORE_ERROR])) {
error_log(sprintf(
'[FATAL] %s in %s:%d',
$error['message'],
basename($error['file'] ?? 'unknown'),
$error['line'] ?? 0
));
// 生产环境建议输出 500 页面,开发环境可 echo 错误详情
}
}
});
PHP 7.1 升级到 8.x 后的额外注意点
PHP 7.1 中大部分 Fatal error 仍是引擎级终止;但从 7.2 开始,部分原本 fatal 的场景(如参数类型不匹配、返回值类型不符)已改为抛出 TypeError 异常,可被 catch (TypeError $e) 捕获。但你当前是 7.1,这些改进尚未生效。
所以升级前务必检查:
- 是否依赖已被移除的函数(如
mysql_*、create_function)——这类会直接 fatal,无法绕过 - 是否有未声明的类或命名空间拼写错误——
Class not found是典型 fatal,register_shutdown_function是唯一记录手段 -
error_log配置的路径是否真实可写(如/var/log/php/error.log),且 Web 服务器用户(如 www-data)有权限
真正容易被忽略的是:shutdown 函数里的逻辑必须「无副作用」。哪怕只是多调用一次 json_encode() 或读一个未初始化的全局变量,都可能引发二次错误,导致原始 fatal 信息彻底丢失。先保日志,再谈其他。



















