如何排查PHP异常处理器收到Error而非Exception实例的问题
问题原因
这套15年前的代码是为PHP 5及更早版本编写的,新旧PHP版本的异常处理机制存在不兼容的变更,这是代码平稳运行多年后突然报错的核心原因:
- PHP 5 环境下,
set_exception_handler()注册的全局异常处理器只会接收Exception类的实例。所有运行时致命错误(比如调用不存在的函数、类加载失败、类型不匹配等)不会进入异常处理流程,会直接输出原生错误并终止脚本,根本不会传给自定义的ExceptionHandler函数。 - PHP 7.0 开始官方重构了错误处理模型,新增了
Error类,几乎所有之前会直接终止脚本的致命运行时错误,现在都会以Error实例的形式抛出。Error和Exception是平级的类,共同实现了Throwable接口,两者都会被全局异常处理器捕获。
你当前运行在PHP 7.4环境下,之前PHP5里不会进到异常处理器的致命错误,现在会作为Error实例传给ExceptionHandler,而老代码里ReportException强制要求入参是Exception类型,就触发了看到的类型不匹配致命错误,通常这类问题的触发契机是近期服务器PHP版本从5.x升级到了7.4。
排查&修复步骤
- 首先修正异常处理函数的类型声明,把所有异常处理相关函数的入参类型从
Exception改为Throwable,兼容PHP7+下所有可能抛出的异常和错误:
function ReportException( Throwable $e ) { // 原有业务逻辑保持不变 } function ExceptionHandler( Throwable $e ) { ReportException($e); // 原有业务逻辑保持不变 }
- 在异常处理函数开头增加错误日志打印,定位具体触发Error的位置:可以加代码打印
$e的类名、错误信息、触发文件、触发行号,比如:
// 加到ExceptionHandler最开头,写入日志或者临时输出都可以 error_log(sprintf( "捕获到错误:类型%s,信息%s,文件%s,行号%d", get_class($e), $e->getMessage(), $e->getFile(), $e->getLine() ));
常见的触发原因包括:PHP7移除了老版本内置的函数(比如旧的mysql_*系列数据库函数)、代码里调用了不存在的方法/函数、类文件加载路径错误、传参类型不符合要求、内存超限等。
- 根据日志定位到具体错误点后,针对性修复兼容问题即可,比如把废弃的
mysql_*函数替换为mysqli或PDO实现、修正文件加载路径等。
修复进度记录
更新1:将入参类型从
Exception修改为Throwable后,最初的类型不匹配致命错误不再抛出,但应用仍存在其他运行异常,待进一步定位。
更新2:定位并修复对应触发Error的业务逻辑后,应用已恢复正常运行,无致命错误,修复方案验证有效。
内容的提问来源于stack exchange,提问作者Borea Deitz
相关产品推荐
相关产品推荐

