答案:因为fatal error属于不可恢复的致命错误,php直接中止脚本执行,不触发set_error_handler;其设计上被刻意排除在该函数捕获范围之外。

PHP 7+ 中 set_error_handler 为什么捕获不到 Fatal error
因为 set_error_handler 只处理 E_ERROR 级别以下的错误(如 E_WARNING、E_NOTICE),而 Fatal error 属于“不可恢复的致命错误”,PHP 会直接中止脚本执行,不触发该处理器。
常见现象:调用未定义函数、访问空对象方法、内存耗尽、类重定义等,页面白屏且无日志 —— 这不是没报错,是根本没机会进你的错误处理逻辑。
-
Fatal error不走set_error_handler,必须用register_shutdown_function+error_get_last()捕获 - PHP 7 引入了
Throwable接口,Fatal Error被转为Error子类(如ParseError、FatalError),但仅限于部分场景(如eval中的语法错误),并非所有致命错误都可被try/catch捕获 - 扩展加载失败(如
undefined function curl_init)或 PHP 启动阶段错误(php.ini配置错误)仍无法在运行时捕获
用 register_shutdown_function 捕获最后的致命错误
这是最通用、兼容 PHP 5.6–8.x 的方式。它在脚本终止前强制执行一次回调,配合 error_get_last() 可拿到最后一次发生的错误(包括 Fatal error)。
注意:它只能捕获「当前请求生命周期内」发生的最后一个致命错误,不能拦截或阻止崩溃,仅用于记录和兜底响应。
register_shutdown_function(function () {
if ($error = error_get_last()) {
$type = $error['type'] ?? 0;
$message = $error['message'] ?? '';
$file = $error['file'] ?? '';
$line = $error['line'] ?? 0;
// 只捕获致命类错误
if (in_array($type, [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR])) {
error_log("[FATAL] {$message} in {$file}:{$line}");
// 可选:输出友好提示(开发环境)或返回 500 页面(生产环境)
}
}
});
- 必须放在脚本最开始(如入口文件
index.php顶部),否则可能错过早期致命错误 -
error_get_last()返回的是数组,type是整数,建议用常量比对而非硬编码数字 - 不要在 shutdown 函数里再抛异常或触发新错误,否则会掩盖原始致命错误
PHP 7+ 中 try/catch 能捕获哪些致命错误
PHP 7 将部分致命错误升级为 Throwable(如 TypeError、ParseError、ArithmeticError),这些可以被 catch (Throwable $e) 捕获;但传统运行时致命错误(如 Call to undefined function、Class 'XXX' not found)依然不行。
try {
nonExistentFunction(); // ⚠️ 仍会 fatal,无法 catch
$a = new NonExistentClass(); // ⚠️ 同样 fatal
} catch (TypeError $e) {
// 可捕获:参数类型不匹配、返回值类型错误等
} catch (ParseError $e) {
// 可捕获:eval() 中的语法错误
} catch (Throwable $e) {
// 覆盖其他所有 Error/Exception,但对大多数 Fatal error 无效
}
-
try/catch对自动加载失败(class not found)完全无效,必须靠spl_autoload_register或 Composer 的 autoload 错误处理兜底 -
ParseError仅在eval()或动态加载代码时抛出,普通文件语法错误发生在解析阶段,早于脚本执行,无法 catch - 过度依赖
catch (Throwable)容易产生“已捕获”的错觉,实际线上仍可能白屏
生产环境真正可靠的致命错误兜底方案
单一机制无法覆盖所有致命错误场景。可靠做法是分层防御:
- 入口文件顶部注册
register_shutdown_function,记录error_get_last()并返回统一 500 响应体 - 启用 PHP 的
log_errors = On和error_log = /path/to/php-error.log,确保底层错误至少落盘 - Web 服务器(Nginx/Apache)配置
fastcgi_intercept_errors on+ 自定义 500 页面,防止 PHP 白屏暴露路径或版本信息 - 关键流程(如 API 入口)手动加
if (function_exists('xxx')) { ... }或class_exists('XXX')预检,把致命错误提前转为可控异常
最易被忽略的一点:register_shutdown_function 本身也可能被禁用(如 disable_functions = register_shutdown_function),上线前务必检查 phpinfo() 或 ini_get('disable_functions')。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











