php 7+ 必须用 catch (throwable $t) 统一处理 error 和 exception,set_exception_handler 与 set_error_handler 均需适配 throwable;致命错误需 register_shutdown_function + error_get_last() 补漏,全局处理器中避免依赖超全局变量。

PHP 7+ 中 Error 和 Exception 必须统一处理
PHP 7 起,Error(如 TypeError、ParseError)和 Exception 都实现了 Throwable 接口,这意味着你不能再只捕获 Exception 就高枕无忧——漏掉 Error 会导致未捕获异常直接崩溃进程。
常见错误现象:本地开发时 try-catch (Exception $e) 看似正常,上线后遇到类型不匹配却触发 500 页面,日志里只有“Fatal error: Uncaught TypeError”,没进你的异常处理器。
- 必须用
catch (Throwable $t)作为兜底,而非Exception - 在
set_exception_handler()回调中也应接收Throwable类型参数 - 不要在
catch (Throwable $t)块里再抛出新异常(比如throw new RuntimeException(...)),否则可能触发循环调用或静默失败
set_error_handler() 转换警告为异常要谨慎选级
用 set_error_handler() 把 E_WARNING、E_NOTICE 等转成异常看似“统一”,但实际会显著改变程序行为:原本可继续执行的警告,变成异常后会中断流程。
使用场景:仅推荐在严格模式调试、CI 流水线或灰度环境启用;生产环境通常不该把 E_NOTICE 当异常处理。
- 只建议转换
E_WARNING及以上(排除E_NOTICE、E_DEPRECATED),例如:if ($errno & (E_WARNING | E_ERROR | E_PARSE)) { throw new ErrorException(...); } - 回调函数内必须返回
true,否则 PHP 默认错误处理器还会再执行一次,导致重复日志 - 注意:某些错误(如
E_PARSE)无法被set_error_handler()捕获,只能靠register_shutdown_function()+error_get_last()补漏
全局处理器里不能依赖 $GLOBALS 或 $_SERVER
当 set_exception_handler() 或 set_error_handler() 触发时,脚本可能已处于不稳定状态:超全局变量可能被修改、输出缓冲可能关闭、甚至数据库连接已释放。
容易踩的坑:在异常处理器里直接调用 file_put_contents($_SERVER['DOCUMENT_ROOT'] . '/log.txt', ...),结果因 $_SERVER 不可用或路径不可写而静默失败。
- 所有路径、配置应提前注入处理器闭包,或从常量/配置文件读取,避免运行时依赖超全局变量
- 日志写入优先走异步方式(如
error_log()写 syslog,或发到 Kafka/RabbitMQ),不阻塞主流程 - 若需记录请求上下文(如 URL、method),应在中间件层提前捕获并存入静态属性或
ThreadLocal类似机制(PHP-FPM 下可用static变量配合请求生命周期管理)
框架外裸写时 register_shutdown_function() 是最后一道防线
致命错误(如 Fatal error: Allowed memory size exhausted)不会触发 set_error_handler() 或 set_exception_handler(),只能靠 register_shutdown_function() 检查 error_get_last()。
性能影响:该函数始终注册,但只在脚本终止时执行一次,开销极小;关键是它不能恢复执行,只能做记录和兜底响应。
- 必须判断
error_get_last()返回值是否非空,且type属于致命类(E_ERROR、E_PARSE、E_CORE_ERROR等) - 不要在里面尝试
echo或http_response_code(),此时输出可能已被发送;安全做法是写日志 + 调用fastcgi_finish_request()(PHP-FPM)或直接exit - 与
set_exception_handler()配合时,确保两者不重复记录同一错误(例如先检查是否有未捕获异常,再查 fatal error)
真正难的不是写几个 catch 块,而是让错误发生时,系统仍知道“自己是谁”“刚做了什么”“还能安全地告诉用户什么”。每层处理器都该有明确边界:谁负责记录、谁负责响应、谁负责降级,而不是堆砌 try-catch 后就以为万事大吉。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











