composer analyse —— 静态分析工具 PHPStan 完全指南

一、PHPStan 是什么?

PHPStan 是 PHP 生态中最广泛采用的静态分析工具,它可以在不运行代码的情况下,通过分析代码的语法、类型和调用链,提前发现潜在的逻辑错误和 Bug。是现代 PHP 项目的标配工具之一。

核心价值

PHPStan 的核心价值在于在代码运行前发现问题。与单元测试不同,单元测试需要编写测试用例并实际执行代码,而静态分析则通过扫描源码即可完成检查,成本更低、覆盖更广。

例如,假设有以下代码:

public function getUser(int $id): User
{
    return User::find($id);
}

User::find() 在未找到记录时可能返回 null,但方法声明只返回 User 类型。PHPStan 会立即报告:

Method getUser() should return User but returns User|null.

这种类型不匹配问题如果不经静态分析,可能要等到生产环境运行时才会暴露。

PHPStan 的检查级别

PHPStan 采用0–9 共 10 个检查级别,级别越高检查越严格:

级别检查内容
0基础检查:未定义的类、函数、方法调用
1未定义的变量、未知的魔术方法
2返回值类型、参数类型(基于 PHPDoc)
3死代码检测、恒真/恒假条件
4方法调用时的参数类型检查
5强制类型提示(missing typehints)
6+并集类型严格检查(逐步增加严格度)

渐进式采用的策略:对于已有项目,建议从 level 0 或 level 1 开始,逐步提升。每次提升级别时,如果错误过多,可先生成基线文件(baseline)将现有问题暂时忽略,之后只关注新增问题。


二、真实场景配置与使用

场景示例:通用 PHP 项目的 PHPStan 配置

对于非 Laravel 的 PHP 项目(如 Hyperf、Symfony、原生 PHP 应用),标准 PHPStan 配置即可满足需求。

步骤一:安装

composer require --dev phpstan/phpstan

步骤二:创建配置文件 phpstan.neon.dist

parameters:
    phpVersion: 70400          # 指定项目最低 PHP 版本
    level: 7                   # 推荐 level 7 作为严格检查起点
    paths:
        - src
        - tests
    excludePaths:
        - src/legacy
        - tests/fixtures
    scanDirectories:
        - vendor/your-framework/src   # 如需要解析框架辅助函数

scanDirectories 的使用场景:如果项目依赖大量全局辅助函数(如 env()、config()、service()),且这些函数定义在框架源码中,PHPStan 需要扫描框架源码才能正确解析这些函数。这时将框架源码目录加入 scanDirectories,PHPStan 就能发现所有框架提供的函数和类定义,而不会将框架本身作为分析目标。

步骤三:在 composer.json 中定义脚本

{
    "scripts": {
        "analyse": "vendor/bin/phpstan analyse --memory-limit=-1"
    }
}

--memory-limit=-1 表示不限制内存,适用于大型项目。

步骤四:运行分析

composer analyse

三、高级配置详解

3.1 并行处理配置

PHPStan 默认启用多线程并行处理,以充分利用多核 CPU:

parameters:
    parallel:
        jobSize: 20                    # 每个作业处理的文件数
        maximumNumberOfProcesses: 8    # 最大并行进程数
        minimumNumberOfJobsPerProcess: 2
        processTimeout: 600.0          # 进程超时时间(秒)
        loadLimit: 0.7                 # 可用 CPU 使用率上限
  • 如果遇到 Child process timed out 错误,适当增加 processTimeout
  • 在资源受限环境中,可设置 maximumNumberOfProcesses: 1 关闭并行

3.2 生成基线(Baseline)

对于已有项目,一次性修复所有问题不现实。PHPStan 支持生成基线文件:

vendor/bin/phpstan analyse --generate-baseline

执行后会生成 phpstan-baseline.neon 文件,记录当前所有错误。后续运行 PHPStan 时,这些错误会被忽略,只会报告新增错误。在配置文件中引用基线:

includes:
    - phpstan-baseline.neon

适用场景:遗留项目升级检查级别时,可以先稳定在低级别,逐步修复基线中的问题。

3.3 编辑器集成(PhpStorm)

PhpStorm 内置了 PHPStan 集成支持,使用 Composer 安装 PHPStan 后,IDE 会自动检测并启用:

  1. PhpStorm 会自动检测 vendor/bin/phpstan 可执行文件
  2. 在 Settings → PHP → Quality Tools → PHPStan 中可配置 PHP 解释器和 PHPStan 路径
  3. 启用后,PHPStan 的检查结果会实时高亮显示在编辑器中,并与 PhpStorm 原生检查区分(带有 phpstan 前缀)
  4. 批量运行时,错误会显示在 Problems 工具窗口中

对于 Docker Compose 环境,需要确保使用 docker-compose exec 模式。

3.4 扩展 PHPStan:数据提取能力

PHPStan 不仅用于错误检查,还可以提取代码库的结构化数据,用于生成文档、调用图等。

核心机制是通过三个扩展点配合工作:

  1. Collector(收集器):遍历 AST,收集数据
  2. Rule(规则):处理收集到的数据,包装为带元数据的规则错误
  3. Error Formatter(错误格式化器):将元数据输出为 JSON 等格式

例如,stella-maris/callmap 扩展利用此机制生成整个项目的方法调用映射,可视化代码依赖关系。


四、IDE 实时检查配置

除了 PhpStorm 的内置集成,VS Code、Vim、Emacs 等编辑器也支持通过 LSP 或插件实现 PHPStan 实时检查。

推荐配置要点:

  1. 在编辑器中启用 PHPStan 实时检查后,编 辑器界面应分割显示,一边是代码,一边是分析结果
  2. 实时反馈能大幅提升开发效率,实现「与 PHPStan 结对编程」的体验

五、完整案例:为 Hyperf 项目配置 PHPStan

场景:为 Hyperf 框架项目配置静态分析,从 level 0 起步,逐步提升到 level 7。

步骤一:安装

composer require --dev phpstan/phpstan

步骤二:创建 phpstan.neon.dist

parameters:
    phpVersion: 80100           # PHP 8.1
    level: 0                    # 从最低级别开始
    paths:
        - app
    excludePaths:
        - app/config
        - app/migrations
    scanDirectories:
        - vendor/hyperf/framework/src
        - vendor/hyperf/utils/src
    # 忽略缺失的 PHPDoc 类型
    checkMissingIterableValueType: false
    checkGenericClassInNonGenericObjectType: false

步骤三:首次运行并生成基线

vendor/bin/phpstan analyse --generate-baseline

步骤四:在 composer.json 中添加脚本

{
    "scripts": {
        "analyse": "vendor/bin/phpstan analyse --memory-limit=-1"
    }
}

步骤五:逐步提升级别

在 phpstan.neon.dist 中将 level 从 0 改为 1,运行 composer analyse,修复新增错误。重复此过程,直到达到目标级别(建议至少 level 6)。

步骤六:集成到 CI 流程

# .github/workflows/ci.yml
- name: Run PHPStan
  run: composer analyse

六、常见问题与解决方案

问题解决方案
Allowed memory size exhausted设置 --memory-limit=2G 或 -1
Child process timed out增加 parallel.processTimeout 配置
框架辅助函数无法解析使用 scanDirectories 扫描框架源码
魔术方法/属性报错(Laravel Eloquent)使用 Larastan 替代原生 PHPStan
错误过多无法一次性修复使用 --generate-baseline 生成基线
检查级别升级困难逐级提升,每级稳定后再升一级

七、总结

PHPStan 作为 PHP 静态分析的标准工具,通过可渐进提升的检查级别、基线管理和丰富的扩展生态,能够适配从全新项目到遗留系统的各种场景。配合 IDE 集成和 CI 自动化,它可以成为代码质量保障体系中不可或缺的一环。

最佳实践建议:

  1. 新项目:直接使用 level 7 或 level 8
  2. 遗留项目:从 level 0 起步,每稳定一个级别提升一级
  3. Laravel 项目:使用 Larastan 替代原生配置
  4. CI 集成:作为 PR 合并的门禁条件,检查不通过则阻止合并
  5. 编辑器集成:启用 IDE 实时检查,在编码过程中即时获得反馈