生产环境突然甩出 `Class 'Redis' not found`,我却在查 Composer 依赖
上周凌晨两点,监控群里突然炸了。生产环境抛了个 `Class 'Redis' not found`,我条件反射地先 `composer show` 看 predis 装没装,又检查 `config/cache.php` 里的驱动配置,甚至怀疑是 Docker 镜像构建时 layer 缓存搞丢了扩展。折腾了四十分钟,最后 SSH 进容器一敲 `php -m | grep redis`,空的。才想起来上周运维同事为了缩减镜像体积,把 `php-redis` 扩展从 Dockerfile 里注释掉了,测试环境因为挂载了本地 PHP 环境没暴露这个问题。
这种报错最坑的地方在于,它跟 Composer 依赖半毛钱关系没有,却长得像极了自动加载失败。我后来给自己整了个"报错首诊"的粗糙原则:看到 `Class not found`,先分清楚是 **Composer 找不到**(路径、命名空间、autoload 没 dump)、**PHP 扩展没装**(Redis、Swoole、GD 这类)、还是 **运行时扩展被禁用**(php.ini 里 `disable_functions` 或者 CLI/FPM 配置不一致)。这三类"病因"治疗方案完全不同,混着查就是浪费时间。
另一个让我长记性的是 `SQLSTATE[HY000] [2002] Connection refused`。新手时期我盯着数据库账号密码核对了八遍,防火墙规则翻了个底朝天,最后发现是 MySQL 8.0 升级后 socket 路径变了,而项目里某个老模块还在用 `localhost` 走 socket 连接,新环境配的是 `127.0.0.1` 走 TCP。现在我的习惯是:遇到数据库连接报错,先确认用的是 **host 还是 socket**、**端口是否被占用**、**客户端库版本是否匹配服务端**(MySQL 8 的认证插件坑过多少人),这三步走完再去看权限和网络。
还有 ThinkPHP 里那个经典的 `Controller not found: app\controller\Index`。框架把命名空间转控制器路径时,大小写敏感问题在 Linux 生产环境会突然发作。我本地 Windows 开发,`Index.php` 大写 I 能跑,上线就 404。现在部署脚本里必加一条 `find app/controller -name "*.php" | xargs -I {} sh -c 'echo {} | grep -q "[A-Z]" && echo "警告:控制器文件名含大写 {}"'`。
最近整理了个极简对照表贴在显示器边上,不是完整解决方案,是**第一反应该往哪查**:
Exception 含 "Class"/"Trait"/"Interface" + "not found"
→ 先 `php -m` 筛扩展,再 `composer dump-autoload`,最后看大小写/命名空间
数据库连接类报错(2002/1045/1049)
→ 先 `telnet host port` 确认通不通,再看 socket vs TCP,最后才查账号权限
ThinkPHP "Controller/Model not found"
→ 本地 `php think route:list` 能过不代表 Linux 能过,文件名大小写是头号嫌犯
缓存/队列突然失效且无任何报错
→ 先看 Redis 内存是否触顶淘汰、再看序列化方式是否变更(`igbinary` 和 `php` 混用会反序列化失败但静默)
上传/导出功能本地正常、线上空白/超时
→ `post_max_size`、`memory_limit`、`max_execution_time` 三兄弟,FPM 和 CLI 配置可能各唱各的
这表肯定不全,但核心思路就一个:别让报错信息牵着鼻子走,先按"运行时环境 → 框架配置 → 业务代码"的顺序分层定位。很多报错长得像代码问题,根子却在环境差异上。凌晨两点的大脑不适合做深度推理,有个粗糙的检查清单,至少能保证不往反方向狂奔。
你们有没有被某个报错的外表骗过、实际病因完全在另一个层级的经历?我那个 Redis 扩展的锅,到现在想起来还觉得脸红。

