php异常无法捕获e_warning、e_notice及fatal error,因exception与error是平行类,仅catch(throwable)可统一捕获;fatal error须用register_shutdown_function+error_get_last()捕获。

PHP 的异常不能捕获 E_WARNING、E_NOTICE 这类错误,也不能捕获 Fatal Error;想靠一个 try-catch 拦住所有问题,一定会漏掉关键崩溃点。
catch(Exception $e) 为什么拦不住 Warning 和 Fatal Error
PHP 的 Exception 和 Error 是两个平行类,都实现 Throwable 接口,但 try-catch(Exception $e) 只匹配 Exception 及其子类。而 E_WARNING、E_NOTICE 属于错误(Error 类或其子类),Fatal Error 更是直接终止执行、不进 catch 块。
常见误判场景:
- 用
file_get_contents('missing.txt')触发Warning: failed to open stream→ 不抛Exception,catch(Exception)完全无效 - 调用未定义函数
undefined_function()→Fatal error: Uncaught Error→ 脚本立刻终止,catch根本没机会运行 -
json_decode($bad_json, true, 512, JSON_THROW_ON_ERROR)在 PHP 7.3+ 才抛JsonException,旧版本默认静默返回null
set_exception_handler() 能兜底,但不能替代 try-catch
set_exception_handler() 只处理「未被捕获的 Exception」,对 Error 依然无能为力。它不是万能 fallback,而是最后一道业务级异常防线。
典型配置误区:
- 只注册
set_exception_handler(),却没在关键路径加try-catch→ 异常直接上浮,日志里只有顶层堆栈,丢失上下文 - 在
set_exception_handler()中调用exit或die→ 阻断了后续清理逻辑(如关闭数据库连接、释放临时文件) - 没检查
$e instanceof Throwable就直接调getMessage()→ PHP 7 之前不兼容,且某些自定义异常可能重写了该方法但未保障返回字符串
Fatal Error 怎么捕获:register_shutdown_function + error_get_last()
真正能抓到 Fatal Error 的,只有 register_shutdown_function() 配合 error_get_last()。它在脚本终止前强制执行一次,是唯一可行入口。
实操要点:
- 必须在脚本最开始就注册,否则中途 include 的文件出错时可能已失效
-
error_get_last()返回null表示无致命错误;返回数组时需检查type字段是否为E_ERROR、E_PARSE、E_CORE_ERROR等 - 不要在 shutdown 函数里再抛异常或触发新错误,否则会二次崩溃,无法记录原始错误
- 示例片段:
register_shutdown_function(function () { $error = error_get_last(); if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) { error_log("FATAL: {$error['message']} in {$error['file']} on {$error['line']}"); // 此处可输出友错页,但避免 echo 或 exit } });
错误和异常混用时的兼容陷阱
很多老项目用 set_error_handler() 把 Warning 转成 Exception,但这不是银弹:
- PHP 内置函数(如
fopen、mysqli_query)默认不抛异常,除非显式启用(如mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)) -
@错误抑制符会绕过set_error_handler(),导致你转异常的逻辑完全失效 - 在
__destruct()或扩展回调中触发的错误,可能无法被set_error_handler()捕获 - PHP 8.0+ 引入
TypeError、ValueError等Error子类,它们仍不能被catch(Exception)捕获,必须写成catch(Throwable)或分别声明
最易被忽略的一点:catch(Throwable) 在 PHP 7+ 才可用,且会同时捕获 Exception 和 Error —— 但你不该在业务逻辑里 indiscriminately 捕所有 Throwable,因为 ParseError 或 FatalError 往往意味着代码已损坏,强行恢复反而掩盖真实问题。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











