### [🐘 遗留 PHP 项目冲 Level 9?这份渐进式启用路线请收好](https://www.huociguo.com/article/217) **Published:** 2026-07-22T16:37:53 **Author:** 米了 **Excerpt:** PHPStan 1.0 起级别体系扩展到 0–9,其中 level 9 在 level 8 基础上增加了一项关键检查:严格的 mixed 类型比较——所有 mixed 参与的操作都会被视为错误,等价于开启了最高严格度(配置中也可用 leve PHPStan 1.0 起级别体系扩展到 **0–9**,其中 **level 9 在 level 8 基础上增加了一项关键检查:严格的** `mixed` **类型比较**——所有 `mixed` 参与的操作都会被视为错误,等价于开启了最高严格度(配置中也可用 `level: max` 作为别名)。对全新项目,从第一天就锚定 level 8/9 是合理目标;但对动辄数万行、类型注解残缺的遗留代码库,直接 `--level=9` 往往会瞬间爆出几千条错误,团队信心直接劝退 。 ![](https://api.huociguo.com/wp-content/uploads/2026/07/%E9%81%97%E7%95%99PHP%E9%A1%B9%E7%9B%AE%E5%8D%87%E7%BA%A7%E8%B7%AF%E7%BA%BF%E5%9B%BE-2216b42ac7-1.png "遗留PHP项目升级路线图-2216b42ac7-1") ## 🎯 核心认知:Level 9 不是起点,而是终点 PHPStan 各级的递进关系: - **Level 0–2**:未知类、未定义变量、未知方法 - **Level 3–4**:返回值类型检查、死代码检测 - **Level 5–6**:方法参数类型校验、缺失类型提示告警 - **Level 7–8**:联合类型收窄、nullable 严格检查 - **Level 9**:`mixed` 类型视为错误,**要求代码完全类型化**​ > 💡 在遗留项目里,正确姿势是”从代码库当前能通过的级别起步,逐级抬升”,而不是反过来 。 ## 🛠️ 五步渐进式启用策略 ### 第一步:选一个”能跑通”的起始级别 不要一上来就 level 9。先跑: ```bash vendor/bin/phpstan analyse --level=6 src ``` 选一个团队当前能承受、错误量可控的级别作为起点(多数遗留项目从 5 或 6 起步比较现实)。 ### 第二步:生成基线,冻结存量债务 ```bash vendor/bin/phpstan analyse --level=8 --generate-baseline ``` 这条命令会产出 `phpstan-baseline.neon`,记录当下所有错误及其在每个文件中的出现次数 。在主配置中引入: ```neon includes: - phpstan-baseline.neon parameters: level: 8 paths: - src excludePaths: - src/Legacy - database/migrations/* ``` 此时再跑分析,存量错误全部被抑制,**PHPStan 只对新代码中的违规说不**——基线的本质是一份”技术债务台账”,而不是永久豁免牌 。 ### 第三步:接进 CI,形成”防回流”闸门 把 PHPStan 挂到持续集成流程中。基线存在的前提下,CI 对存量代码是通过的,但**任何 PR 引入的新错误都会让构建失败**​ 。这一步至关重要:它把静态分析从”一次性大扫除”变成了”棘轮”,只进不退。 ### 第四步:按文件/模块啃基线,而非按错误类型 很多团队的踩坑点:两人同时修不同错误后各自 regenerate baseline,造成 `phpstan-baseline.neon` 的合并冲突。 正确做法: 1. **按文件/模块分配**:每人认领几个文件,彻底修完 2. **修完一批就 regenerate**:`vendor/bin/phpstan analyse --level=8 --generate-baseline` 3. **观察基线文件行数下降**——这是遗留项目最爽的进度条 配合 `reportUnmatchedIgnoredErrors: true`,可以清理掉那些已经不匹配任何错误的陈旧忽略规则,避免它们悄悄掩盖后续新引入的问题 。 ### 第五步:基线清空后,抬到下一级 当 level N 的基线条目归零,把配置切到 level N+1,重新生成基线,循环往复。如此一步步逼近 level 9。整个过程可能跨越多个 sprint,但业务功能开发完全不被阻塞 。 ## ⚠️ 三个容易翻车的点 **1\. 别用** `@phpstan-ignore` **无脑压错误** PHPStan 2.1.41+ 已支持 `reportIgnoresWithoutComments`,强制要求每个忽略都附带解释性注释,且不再允许 `@phpstan-ignore-line` / `@phpstan-ignore-next-line` 这种无标识的写法 。这倒逼团队想清楚”为什么忽略”,而不是把静态分析变成自欺欺人。 **2\. 排除真正的”噪音源”** 自动生成的迁移文件、API spec 生成的 DTO、框架魔法方法密集的 Model 层,可通过 `excludePaths` 排除或借助框架专用扩展(如 Laravel 的 Larastan、Symfony 的 phpstan/phpstan-symfony)增强类型推断 。 **3\. 严格规则包是补充,不是替代** ```bash composer require phpstan/phpstan-strict-rules --dev ``` ```neon includes: - vendor/phpstan/phpstan-strict-rules/rules.neon ``` 它在级别体系之外追加了”条件语句必须布尔”、”禁止松散比较 `==`“等规则 ,属于锦上添花,不能代替 level 9 本身的 `mixed` 严格检查。 ## 📊 预期收益与节奏 经验数据:对于一个 8 万行左右的遗留代码库,从 level 5 起步、每季度抬升 1–2 级、每 sprint 分配 15–20% 工时啃基线,通常 **3–4 个季度可稳定抵达 level 8**,向 level 9 收敛则需更长的类型补全周期,尤其是补全函数返回值、属性类型、集合泛型这三个重灾区。 > 📌 衡量成功的指标不是”今天到了哪一级”,而是”基线文件在不在缩小”。只要基线逐月变矮,方向就是对的。 PHPStan level 9 不是一个开关,而是一种纪律。基线让遗留项目”先止血、再疗伤”,逐级抬升把大目标拆成可交付的小步进,CI 闸门保证债务不新增——三者配合,level 9 才从空中楼阁变成可抵达的彼岸。 **Categories:** PHP教程 ---