PHP Fatal error 与 Exception 混着来?我按"报错脸色"分了类,现在一眼就知道该先查哪儿

小助手
小助手 版主圣羽星庭 勋望元宿志愿先锋
社区管理
站长杂谈 54 浏览 4 回复

前两天凌晨两点,测试群突然炸了,截图里红彤彤一片。我眯着眼一看,好家伙,同一屏里挤着 Class 'Redis' not foundTrying to access array offset on value of type null、还有一行带堆栈的 think\exception\ErrorException。新人@我说"全是Exception",我让他先区分"谁报的"再谈排查,对面沉默了三分钟。

这事让我想通一件事:PHP 的报错不是同一种"红",乱扑上去容易南辕北辙。我按自己的习惯,把常见报错按"脸色"分了四类,对应四种第一反应——

一、白脸:Parse error / Syntax error,编译期就挂了

特征:行号明确,通常带 unexpected 'xxx',页面直接 500,连框架兜底都进不去。

第一反应:别盯着那行死磕,往上扫十行。90% 是上一行少了个分号、括号没配对,或者 PHP 8 里混了老项目的 array 简写语法。我上周栽过一回,本地 PHP 7.4,线上 8.2,match 当变量名用了,本地没事,上线白屏。

提速技巧:本地开 php -l filename.php 做语法预检,CI 里加一步,能拦住一半低级事故。

二、红脸:Fatal error,运行时撞墙

典型如 Class not foundCall to undefined functionAllowed memory size

这里要拆两条线:是"找不到"还是"不够用"。

"找不到"先想 autoload。ThinkPHP 里我踩过最阴的坑:命令行生成的中间件文件,命名空间自动带了 app\middleware,但我手贱挪到 app\common\middleware,文件名没改,composer dump-autoload 三遍都没用——因为 TP 的类库映射优先读 vendor/composer/autoload_psr4.php 里的缓存,不是实时扫目录。

"不够用"看上下文。内存溢出如果是导 Excel,先怀疑 PhpSpreadsheet 的流式读取开关;如果是图片处理,看 GD 还是 Imagick,后者内存泄漏更隐蔽。我现在的习惯:出 memory_limit 报错时,先加 memory_get_usage(true) 打点位,比盲目改 php.ini 有用。

三、黄脸:Warning / Notice,程序没死但数据脏了

这类最烦,页面能刷出来,日志里悄悄埋雷。ThinkPHP 默认开了 error_reporting = E_ALL,开发时满屏黄标,很多人顺手改成 E_ERROR,上线后才发现某个接口返回的 JSON 里混了 undefined index,小程序解析直接崩。

我的做法:本地保持 E_ALL,但给框架配个自定义异常接管,把 Warning 按业务分级。比如数组越界如果是配置读取,抛业务异常;如果是模板变量,记日志但不阻断。关键是别让 Warning 变成"狼来了",否则真出事时日志里翻不出来。

一个具体案例:之前用 file_get_contents 调第三方 API,对方偶发 502,返回了 HTML 错误页。我没判 HTTP 状态码,直接 json_decode,结果 file_get_contents 没报错,json_decode 返回 null,下游拿 null 当数组用,Notice 攒了三天,直到用户投诉数据对不上。

四、青脸:框架封装的 Exception,堆栈最长信息最密

TP 的 think\exception\DbExceptionHttpExceptionValidateException 都属于这类。特征是堆栈深、带 SQL 语句、或者指向框架核心文件。

新人常犯的错误:从堆栈顶往下看。其实应该反着来,从下往上找第一个属于自己项目的文件。比如 DbException 提示 Column not found,堆栈顶在 think\db\Connection,但真正的锅在往上数第四行,你某个模型里写了 ->field('xxx') 而字段早被 DBA 删了。

我现在的条件反射:

- 看到 SQLSTATE[HY000] 开头 → 先复制 SQL 去客户端跑一遍,确认是语法还是数据问题
- 看到 think\exception\ValidateException → 检查路由参数和验证器场景是否匹配,别在 edit 场景里用 create 的必填规则
- 看到 ReflectionException → 99% 是依赖注入的类名写错,或者那个类在命令行环境下没加载(比如计划任务里用了 HTTP 专属的中间件)

最后说个排查顺序的私货

我现在接到报错,按这个顺序扫一眼,基本能定方向:

1. 有没有行号?没有 → 大概率是框架层或配置级问题,查中间件、事件、服务注册
2. 行号指向 vendor 还是 app?vendor → 往上翻自己的调用栈;app → 直接看上下文
3. 报错里带不带 SQL/文件路径/类名?带具体资源 → 资源权限或存在性问题;纯代码 → 逻辑或语法问题
4. 是必现还是偶发?偶发 → 想并发、缓存、第三方依赖;必现 → 本地复现,断点跟进

这套分类不严谨,但胜在快。凌晨两点那种场景,先按"脸色"归类,比盯着堆栈满屏红字发呆强。

你们有没有遇到过"报错类型和实际原因八竿子打不着"的情况?我印象最深的一次是 Maximum execution time exceeded,最后发现是 DNS 解析超时,PHP 进程卡在 fsockopen,CPU 没占多少,时间全耗没了。

评论4
回复 · 4
WadeYang
WadeYang 新手 · #4 ·
已解决,谢谢楼主
知夏45
知夏45 新手 · #3 ·
已解决,谢谢楼主
🌙旅人🧡
🌙旅人🧡 新手 · #2 ·
同求,期待更新
知夏45
知夏45 新手 · #1 ·
已解决,谢谢楼主
微信客服 微信客服