
PHP 里哪些错误能被 set_error_handler 拦住
不是所有错误都能“恢复”——只有被错误处理器捕获到的,才可能通过返回 true 阻止默认行为(比如中止脚本)。但 PHP 的错误分三类:E_ERROR、E_PARSE、E_CORE_ERROR 等致命错误,根本进不了自定义处理器;而 E_WARNING、E_NOTICE、E_USER_WARNING 这类非致命错误,才能被 set_error_handler 接管。
实操上要注意:
-
set_error_handler默认不处理E_ERROR及其子类,哪怕你显式传了E_ALL也没用 - PHP 8.0+ 开始,
E_DEPRECATED和E_USER_DEPRECATED也能被捕获,但依然不能“恢复”已发生的解析失败或内存耗尽 - 若在错误处理器里抛出异常(比如
throw new RuntimeException()),且没被try/catch拦住,会直接升级为未捕获异常,脚本照样终止
用 error_get_last() 判断刚发生的错误是否可干预
error_get_last() 不是实时监听器,它只返回**最近一次执行后遗留的错误信息**,常用于 eval()、include 或函数调用后立刻检查。但它无法区分“这个错误是否已被处理过”,更不能告诉你“还能不能救”。
典型误用场景:
立即学习“PHP免费学习笔记(深入)”;
- 在
set_error_handler回调里调用error_get_last()—— 返回值往往是上一次错误,不是当前这次 - 期望靠它判断
file_get_contents()失败后能否重试 —— 实际应检查返回值是否为false,而不是依赖错误日志 - 忽略错误类型:即使
error_get_last()返回了数组,['type']是E_WARNING才代表有机会绕过;若是E_COMPILE_ERROR,说明文件压根没加载成功,后续逻辑已不可达
为什么 try/catch 拦不住 E_WARNING
PHP 的异常机制和错误机制是两套平行系统。try/catch 只捕获 Throwable(包括 Exception 和 Error),而传统错误(E_WARNING 等)默认不转成异常。想让它们进 catch,必须主动转换。
可行做法:
- 用
set_error_handler把指定错误类型转成ErrorException,再throw出去 - 注意转换范围:别把
E_NOTICE也扔进catch,否则正常调试信息会打断流程 - PHP 7+ 的
Error类(如ParseError、TypeError)天生可被catch,但它们不属于传统“错误等级”,而是语言层面的异常变体
示例关键片段:
set_error_handler(function($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) return;
throw new ErrorException($message, 0, $severity, $file, $line);
});
可恢复性的真正边界在哪儿
所谓“可恢复”,本质是看错误发生时 PHP 是否还维持着完整的执行上下文。一旦 Zend 引擎决定终止当前请求(比如遇到 E_COMPILE_ERROR、内存分配失败、扩展崩溃),就没有“恢复”一说——不是代码写得不够巧,是进程状态已经损坏。
容易被忽略的硬限制:
-
register_shutdown_function()能在脚本结束前运行,但它看到的是最终态,无法逆转已发生的致命错误 - 错误处理器里调用
ob_start()或修改响应头,对E_ERROR无效,因为输出缓冲可能已被销毁 - 某些 SAPI(如 CLI)下,
E_ERROR会直接退出进程;而 Web SAPI 下虽可能返回 500,但后续请求不受影响——这不是“恢复”,只是隔离
真正在意“恢复”的场景,往往该换思路:把高危操作包进子进程、用 pcntl_fork 隔离,或者提前用 function_exists()、extension_loaded() 做守卫,而不是指望错误发生后再补救。











