这篇文章写给刚接手"祖传 PHP 单体"的人:没有文档、没人敢动、每次上线都心惊胆战。过去多年我改造过多个这类系统(ecshop 式魔法全局变量、ThinkPHP 3 老项目、Smarty 模板混 SQL 的都有),总结出一条核心原则:先续命,再现代化;永远渐进式,不推倒重写。
第 0 步:先体检,再动手(1-2 周)
改造前必答的 5 个自查问题:
- 入口清单:有多少个对外 HTTP 入口?CLI 定时任务有哪些?(
grep -r "$_GET\|$_POST" --include="*.php" | wc -l只是开始) - 数据库现状:多少张表?有多少存储过程/触发器/外键?有没有表没有任何代码引用(死表)?
- 依赖存亡:composer.json 里有多少个已停止维护的包?PHP 版本离 EOL 还有几个月?
- 部署方式:是手工 FTP 上传还是有 CI/CD?回滚需要多久?
- 测试覆盖:除了
php -l语法检查,还有没有任何自动化测试?
把这 5 个答案写成一页纸,这就是你的改造基线。没有基线的改造 = 蒙眼开刀。
第 1 步:续命三件套(不改业务代码)
- 上版本控制 + CI:哪怕只是 git + 每次提交跑
php -l+ composer validate。手工 FTP 上传的时代必须先结束。 - 加监控:错误日志聚合(Sentry 免费档够用)+ 慢查询日志打开 + 基本的存活探针。改造期间你会需要知道"是我改坏的还是本来就坏"。
- 包一层 Smoke 测试:把 TOP 20 高频用户路径用脚本录下来(curl 也行),每次部署后跑一遍。这就是你最初的"防护网",比单元测试性价比高 10 倍。
第 2 步:绞杀者模式(Strangler Fig),不是重写
- 新需求一律写到新的分层结构里(哪怕还是同一个 PHP 项目:路由层 → Service 层 → Repository 层),老代码只在被迫修改时才动
- 老入口统一加一个薄 Front Controller,为将来按 URL 逐步分流做准备
- 鉴权先统一:老系统常见的"每个页面自己查 session"必须先收口成一个中间件,否则新老系统永远接不通
第 3 步:数据层是最后的战场,也是最不能先动的
- 先做只读收敛:所有 SQL 从页面代码里挪到统一的 DAO/Repository 类(可以用正则+人工复核批量做)
- 不急着拆库。跨库事务的复杂度会立刻吃掉你全部收益
- 真要拆:先按"读扩散"拆(新系统只读老库),写路径最后切换
第 4 步:退出判据
改造不是无限工程。满足以下任一条就可以停:
- 新需求 100% 落在新结构里,老代码只读不改
- 老代码占比 < 30% 且仍在收缩
- 系统寿命预期 < 改造收益周期(这种情况直接封版续命更划算)
以上是完整路线图的骨架。《PHP 遗留系统改造路线图》(完整版:每步的操作清单、踩坑实录、工具选型对比,持续更新)我整理成了册,一次买断 ¥19.9 永久有效;配套还有《Go 高并发避坑清单》(¥9.9 买断)。都在我的爱发电店铺:afdian.com/a/standsun ,购买后可在评论区直接提问(工作日 24h 内回复)。