平滑过渡老旧php项目异常处理的关键是分层接管:先用set_error_handler统一转换非致命错误为errorexception,再用register_shutdown_function兜底致命错误,逐步替换mysql_*函数并建立结构化日志体系。

老旧 PHP 项目直接套用现代异常处理器会崩,不是因为代码写得差,而是错误来源太杂:E_NOTICE、E_WARNING、裸 mysql_query() 报错、未定义变量、甚至 include 失败——这些在老系统里全靠 @ 抑制或白屏收场。平滑过渡的关键,是分层接管,而不是一上来就 set_exception_handler() 全包。
先接管传统错误,别急着 catch Throwable
PHP 7+ 虽把部分致命错误转成 Error,但 E_NOTICE 和 E_WARNING 依然不进 try/catch。老项目里这类错误极多,强行开启严格异常捕获只会让页面大面积报错。
- 用
set_error_handler()拦住所有E_*级别错误,统一转成ErrorException实例(注意:只对非致命错误生效) - 在 handler 里过滤掉高频无害警告(如
E_USER_DEPRECATED),避免日志爆炸 - 对
E_ERROR和E_PARSE这类真正中断脚本的错误,仍需保留register_shutdown_function()做兜底,因为它们连set_error_handler都进不去 - 示例中不要直接
throw new ErrorException后续又没catch,否则等于把警告升级成致命异常——先记录、再返回 HTTP 200 或 500,视业务容忍度而定
逐步替换 mysql_* 与裸 SQL,否则错误永远不可控
老项目里 mysql_query() 失败时返回 false,但你根本不知道是连接断了、SQL 写错了,还是权限不足。这种错误无法归类,更没法结构化上报。
- 用
grep -r "mysql_" ./扫出全部调用点,按模块分批改用PDO或mysqli,并启用PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION - 不要一步到位改所有 SQL;先确保核心流程(如登录、下单)走新驱动,其他边缘逻辑暂用包装函数桥接
- 旧代码里常见的
or die(mysql_error())必须删掉,它会暴露敏感信息,且无法被任何 handler 捕获 - 若存在拼接 SQL 的逻辑(
"WHERE name = '$name'"),必须同步改成预处理,否则SQLSTATE[HY000]类错误会混在业务异常里,排查成本翻倍
自定义错误码体系要从日志开始,别先设计 Exception 类
很多团队一上来就建 BusinessException、ValidationException,结果上线后发现 80% 的错误来自数据库连接超时、Redis 挂了、第三方 API 返回空——这些根本不是“业务错误”,强行分类反而让日志更难查。
- 先统一日志格式:每条错误日志必须含
error_code(如DB_CONN_TIMEOUT)、level(ERROR/WARN)、trace_id(请求唯一标识) - 从 Nginx/PHP-FPM 错误日志和慢查询日志反推高频错误类型,再决定是否需要抽象成特定
Exception子类 - 避免在
catch块里硬编码字符串错误码;用配置文件或常量类管理,例如ErrorCode::DB_CONN_TIMEOUT - 老系统常见“同一处报错,不同环境显示不同消息”(如本地显示 SQL,线上只显示“操作失败”),根源是没剥离错误展示层——错误码和用户提示文案必须分离
全局 handler 里别做重试或降级,那是业务逻辑的事
看到网上教程让 set_exception_handler() 里自动重试数据库操作,这非常危险。handler 是最后兜底环节,职责只是记录、通知、返回友好响应,不能改变执行流。
-
set_exception_handler()中禁止调用任何可能抛异常的函数(比如再连一次 Redis) - 不要在 handler 里
header("Location: /error")跳转,HTTP 头可能已输出,导致 headers already sent 错误 - 如果要用
try/catch做重试,必须放在具体业务方法内部,比如OrderService::create()里封装三次重试逻辑,而不是扔给全局 handler - handler 输出的 HTML 页面必须极度精简,不含外部 CSS/JS,防止因资源加载失败导致二次错误
最易被忽略的一点:老项目往往没有统一入口,index.php、api.php、admin/index.php 各自为政。错误处理架构再漂亮,只要某个入口没引入初始化脚本,那里的错误就还是白屏或 Warning 直出。上线前务必逐个验证所有可访问路径是否真正接入了新 handler。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











