PHP致命错误无法用set_error_handler捕获,必须用register_shutdown_function+error_get_last()兜底隐屏,并关闭display_errors、开启log_errors写入有权限的日志文件。

PHP 发生致命错误(Fatal error)时默认会直接输出错误信息到页面,暴露路径、函数名甚至数据库配置,不仅影响用户体验,更存在安全风险。要隐屏,不能只靠 error_reporting(0) 或关掉 display_errors —— 这些对致命错误基本无效。
为什么 set_error_handler() 拦不住 Fatal error
因为 PHP 的错误处理机制中,Fatal error(如 Call to undefined function、Class not found、Maximum execution time exceeded)属于“不可捕获的致命错误”,它发生在脚本执行中途且无法恢复,set_error_handler() 根本不会被调用。
-
set_error_handler()只能捕获E_WARNING、E_NOTICE等非致命错误 -
register_shutdown_function()是唯一能在致命错误后执行的钩子 - 必须配合
error_get_last()判断是否真发生了致命错误
用 register_shutdown_function() + error_get_last() 实现隐屏
这是生产环境最可靠的做法:在脚本终止前检查最后错误,如果是致命类,就清空输出缓冲、返回自定义响应(如 500 页面或空白响应),避免泄漏。
register_shutdown_function(function () {
if ($error = error_get_last()) {
$fatalErrors = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR];
if (in_array($error['type'], $fatalErrors)) {
// 清除可能已输出的内容
if (ob_get_level()) ob_end_clean();
// 返回 HTTP 500 状态(可选)
http_response_code(500);
// 输出统一提示(或直接 exit,不输出任何内容)
echo 'Service unavailable';
exit;
}
}
});
- 必须放在所有代码执行前(如入口文件开头),否则可能错过错误
-
ob_end_clean()很关键:防止前面已有部分 HTML 输出导致 header 失败 - 不要在 shutdown 函数里再触发任何可能出错的操作(如写日志文件失败)
display_errors 和 log_errors 的正确组合
隐屏 ≠ 不记录。线上必须关闭 display_errors,但务必开启 log_errors 并指定有效日志路径,否则你连问题都发现不了。
立即学习“PHP免费学习笔记(深入)”;
- PHP 配置中设:
display_errors = Off,log_errors = On,error_log = /var/log/php/error.log - 注意
error_log路径需 Web 服务器用户(如 www-data)有写权限,否则日志静默丢失 - 若用
ini_set()动态设置,需确保在出错前执行;但display_errors对致命错误的屏蔽效果不稳定,不能依赖
真正可靠的隐屏不是靠关掉一个开关,而是靠 shutdown 钩子兜底 + 日志闭环 + 权限与路径校验。最容易被忽略的是 ob_end_clean() 缺失导致 headers already sent 报错,或者日志目录不可写却以为“已经记了”。











