phpstan analyse 在 CI 中失败的常见原因包括:dev 依赖未安装(如未执行 composer install --no-dev)、PHP 版本不匹配(如 Larastan v3 要求 PHP ≥ 8.2)、phpstan.neon 路径错误、paths 配置遗漏 database/ 或 config/ 目录,以及内存不足或基线文件未被显式包含。

phpstan analyse 命令在 CI 中失败,常见原因是什么?
phpstan analyse 在 CI 环境中报错,绝大多数不是代码问题,而是环境或配置缺失。最常踩的坑是:
-
composer install没加--no-dev或--prefer-dist,导致 dev 依赖未装全(尤其是larastan/larastan) - PHP 版本不匹配:Larastan v3 要求 PHP ≥ 8.2,CI 默认镜像常是 8.1 或更低
-
phpstan.neon文件路径错误,比如写成./vendor/larastan/extension.neon,但实际应为vendor/larastan/larastan/extension.neon(注意多一层larastan) -
paths配置里漏了database/或config/,而这些目录下有迁移文件或配置类,Larastan 会尝试分析它们
建议在 CI 步骤中显式指定 PHP 版本,并用完整路径验证配置文件存在:
run: | php -v ls -la vendor/larastan/larastan/extension.neon ./vendor/bin/phpstan analyse --level=5 --configuration=phpstan.neon
GitHub Actions 中如何避免 Larastan 占用过多内存?
CI 环境内存有限,phpstan analyse 默认可能触发 OOM(Out of Memory)。这不是 bug,是分析深度和项目规模的自然结果。
- 不要用
--memory-limit=-1(无限内存),CI runner 会直接 kill 进程 - 推荐加
--memory-limit=1G或2G,并配合--configuration=phpstan.neon确保参数生效 - 如果仍超限,降级
level:把level: 5改成level: 4(Larastan level 5 启用了全部规则,包括模型属性推断等高开销检查) - 更稳妥的做法是拆分分析目标:先跑
app/,再跑tests/,用--paths-file控制范围
示例:
立即学习“PHP免费学习笔记(深入)”;
run: ./vendor/bin/phpstan analyse --memory-limit=1G --configuration=phpstan.neon app/
基线文件(baseline)为什么在 CI 中必须显式引用?
生成的 phpstan-baseline.neon 不会自动生效——它只是个记录文件,必须被 phpstan.neon 显式包含,否则 CI 依然报所有旧错误。
- 错误写法:
./vendor/bin/phpstan analyse --generate-baseline→ 生成文件后就以为完事了 - 正确做法:编辑
phpstan.neon,加上这行:includes: - ./phpstan-baseline.neon
- 注意路径是相对于
phpstan.neon所在位置的;如果放在根目录,就写./phpstan-baseline.neon,别漏掉./ - Git 提交时务必把
phpstan-baseline.neon一起提交,否则其他开发者或 CI 机器读不到
基线不是“跳过检查”,而是把当前已知问题冻结为“已知状态”。新提交引入的任何新错误,仍会原样报出。
Larastan 的 level 5 和 level 4 在 CI 中怎么选?
level 决定检查严格度,也直接影响 CI 通过率和执行时间:
-
level: 4:检查基础类型、空值、未定义方法,适合刚接入或遗留项目,CI 平均耗时 30–90 秒 -
level: 5:额外启用模型属性推断、关系返回类型校验、集合优化建议等,CI 耗时可能翻倍,且容易因 Eloquent 动态属性未声明而报错(如$user->non_existent_field)
选法很简单:
- 新项目或团队已统一规范:直接上
level: 5,配合checkModelProperties: true参数 - 旧项目过渡期:先用
level: 4+ 基线,等核心逻辑稳定后再升到 5 - CI 时间敏感场景(如 PR 检查需 < 2 分钟):固定用
level: 4,主干合并前再用level: 5全量扫描
真正麻烦的不是 level 数字本身,而是 level 5 下 Laravel 的“魔法”特性(如 __get、门面代理)需要更精确的注解支持,否则误报率陡增。这点很容易被忽略。



















