denzyldick/phanalist
GitHub: denzyldick/phanalist
一款基于 Rust 构建的高性能 PHP 静态分析器,通过深度度量复杂度、耦合度和面向对象设计指标来帮助团队控制技术债务并提升代码可维护性。
Stars: 156 | Forks: 8
[](https://crates.io/crates/phanalist)
[](./LICENSE)
[](https://github.com/denzyldick/phanalist/actions)
### 🤔 为什么选择 Phanalist?
PHP 代码库会不断增长。随着规模扩大,它们会积累技术债务 —— 大包大揽的“上帝类”、无人能看懂的冗长方法,以及每次修改都可能崩溃的隐性复杂度。传统的 linter 只能捕获语法错误和代码风格问题,却无法告诉你代码是否**易于维护**。
Phanalist 专注于**结构健康度**。它衡量的是决定长期可维护性的关键因素:
- **复杂度指标** —— 圈复杂度、认知复杂度、每个方法的 LOC、嵌套路径
- **耦合与内聚** —— 迪米特法则违规、上帝类、数据类、扇入/扇出
- **面向对象设计** —— 继承深度、类的加权方法数、类的响应数
- **可读性** —— 注释比率、错误抑制、方法参数数量
可以将其视为针对 PHP 代码的健康体检。它不仅告诉你*哪里*出了问题 —— 每条规则还会解释*为什么*这很重要,以及*如何*修复它。
### ✨ 功能
- 🚀 **极速** —— 基于 Rust 构建,可在几秒钟内分析大型代码库
- 🔍 **31 条内置规则** —— 涵盖复杂度、代码风格、设计模式等
- ⚙️ **开箱即用的零配置** —— 默认即可工作,按需进行定制化配置
- 📄 **多种输出格式** —— `text`、`json`、`sarif`(支持通过 [GitHub Action](https://github.com/marketplace/actions/phanalist) 在 CI pipeline 中实现 PR 内联注释)和 `codeclimate`(适配代码质量平台)
- 🔌 **高扩展性** —— 添加自定义规则仅需几分钟
### 安装
安装 Phanalist 最简单的方法是使用安装脚本:
```
curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/denzyldick/phanalist/main/bin/init.sh | sh
```
它会自动为您的平台下载对应的可执行文件:
```
$ ~/phanalist -V
phanalist 1.0.0
```
此外还有[多种其他安装方式](./docs/installation.md)。
### GitHub Action
使用 [Phanalist GitHub Action](https://github.com/marketplace/actions/phanalist) 为您的 PR 添加内联 SARIF 注释:
```
name: Phanalist
on: [pull_request]
permissions:
security-events: write
jobs:
phanalist:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: denzyldick/phanalist-action@v1
```
### 用法
要分析您的项目源码,请运行:
```
~/phanalist
```
#### 示例

首次运行时会使用默认配置生成 `phanalist.yaml` 文件,并在后续的所有运行中复用该文件。
**其他 CLI 参数:**
| 参数 | 描述 | 默认值 |
|---|---|---|
| `--config`, `-c` | 配置文件路径 | `./phanalist.yaml` |
| `--src`, `-s` | 项目源码路径(可重复使用,例如 `-s src -s tests`) | `./src` |
| `--rules`, `-r` | 仅运行指定的规则(覆盖配置文件) | 来自配置文件 |
| `--output-format`, `-o` | 输出格式:`text`、`json`、`sarif`、`codeclimate` | `text` |
| `--summary-only` | 仅显示每条规则的违规计数 | — |
| `--quiet`, `-q` | 抑制所有输出 | — |
| `--verbose`, `-v` | 提高详细程度;重复使用可增加详细度(`-v` 主流程,`-vv` 解析,`-vvv` 索引) | — |
| `--debug-rule-timing` | 打印每条规则在每个文件上的耗时(最小/最大/平均/p90/p95/p99 + 最慢的文件) | — |
| `--debug-rule-stats` | 打印每条规则的成本/覆盖率统计信息(时间、%、违规数、文件数、语句数) | — |
| `--use-baseline` | 根据 baseline 文件过滤结果,仅报告新增的违规 | — |
| `--update-baseline` | 从当前扫描结果重新生成 baseline(需配合 `--use-baseline` 使用) | — |
### Baseline
Baseline 允许您在现有的代码库中引入 phanalist,而无需一次性修复所有
发现的问题。它会冻结当前的违规记录;后续运行将仅报告新的违规,从而
确保 CI 在已知的历史债务上保持通过(绿色),而在出现新的回归问题时失败。
生成(或重新生成)baseline:
```
~/phanalist --use-baseline phanalist-baseline.json --update-baseline
```
然后基于它运行(在 CI 或本地环境中):
```
~/phanalist --use-baseline phanalist-baseline.json
```
Baseline 是一个格式化输出且排序稳定的 JSON 文件,因此它能产生干净的
差异并方便合并。每个条目以文件、规则和带有计数的稳定消息 ID 作为
键,因此不相关的修改导致的行号偏移不会使其失效,消息文本的措辞
调整也同样不会影响它。当您修复了违规问题后,请重新生成
baseline 以缩减其体积。
### 配置
```
enabled_rules: [] # empty = all rules active
disable_rules: []
exclude_paths: [] # paths skipped before any rule runs (see below)
rules:
E0007:
check_constructor: true
max_parameters: 5
E0009:
max_complexity: 10
E0010:
max_paths: 200
E0012:
include_namespaces:
- "App\\Service\\"
- "App\\Controller\\"
exclude_namespaces: []
reset_interfaces:
- "ResetInterface"
E0015:
threshold: 1
E0016:
max_complexity: 15
E0024:
max_loc: 30
E0025:
max_loc: 500
E0026:
min_ratio: 0.1
max_ratio: 0.5
E0027:
max_methods: 15
max_fields: 10
E0028:
max_getter_setter_ratio: 0.7
min_methods: 3
E0029:
max_fan_out: 10
max_fan_in: 20
E0030:
max_density: 0.3
```
- **`enabled_rules`** —— 要运行的规则白名单(留空 = 全部)
- **`disable_rules`** —— 要跳过的规则
- **`rules`** —— 针对特定规则的配置选项
- **`exclude_paths`** —— 在任何规则运行前跳过的文件,可以是目录前缀(`var/cache`、`bootstrap/cache`)或 glob 模式(`**/*.generated.php`)。这非常适合用于排除框架缓存以及 migrations 等只会产生干扰的固化代码。在 `-v` 详细级别下,如果磁盘上不存在字面量(非 glob)模式,将会触发警告 —— 这有助于捕获拼写错误。未匹配到任何内容的 glob 模式将被静默接受。
### 规则
| 代码 | 名称 | 选项 |
| :--: | :--- | :------ |
| E0000 | 示例规则 | |
| [E0001](/src/rules/examples/e1/e1.md) | 起始标签位置 | |
| [E0002](/src/rules/examples/e2/e2.md) | 空的 catch 块 | |
| [E0003](/src/rules/examples/e3/e3.md) | 方法修饰符 | |
| [E0004](/src/rules/examples/e4/e4.md) | 大写常量 | |
| [E0005](/src/rules/examples/e5/e5.md) | 类名大写 | |
| [E0006](/src/rules/examples/e6/e6.md) | 属性修饰符 | |
| [E0007](/src/rules/examples/e7/e7.md) | 方法参数数量 | `check_constructor: true`, `max_parameters: 5` |
| [E0008](/src/rules/examples/e8/e8.md) | 返回类型签名 | |
| [E0009](/src/rules/examples/e9/e9.md) | 圈复杂度 | `max_complexity: 10` |
| [E0010](/src/rules/examples/e10/e10.md) | Npath 复杂度 | `max_paths: 200` |
| [E0011](/src/rules/examples/e11/e11.md) | 检测错误抑制符 (`@`) | |
| [E0012](/src/rules/examples/e12/e12.md) | Service 与 Shared Memory Model 的兼容性 | `include_namespaces`, `exclude_namespaces`, `reset_interfaces` |
| [E0013](/src/rules/examples/e13/e13.md) | 未被使用的私有方法 | |
| [E0014](/src/rules/examples/e14/e14.md) | 迪米特法则 | |
| [E0015](/src/rules/examples/e15/e15.md) | 方法内聚度缺失 (LCOM4) | `threshold: 1` |
| [E0016](/src/rules/examples/e16/e16.md) | 认知复杂度 | `max_complexity: 15` |
| [E0017](/src/rules/examples/e17/e17.md) | 对象间耦合度 (CBO) | `max_coupling: 10` |
| [E0018](/src/rules/examples/e18/e18.md) | 类的加权方法数 (WMC) | `max_wmc: 50` |
| [E0019](/src/rules/examples/e19/e19.md) | 类的响应数 (RFC) | `max_rfc: 50` |
| [E0020](/src/rules/examples/e20/e20.md) | 继承树深度 (DIT) | `max_depth: 4` |
| [E0021](/src/rules/examples/e21/e21.md) | 子节点数量 (NOC) | `max_children: 15` |
| [E0022](/src/rules/examples/e22/e22.md) | 传入与传出耦合 (Ca/Ce) | `max_ca: 20`, `max_ce: 20` |
| [E0023](/src/rules/examples/e23/e23.md) | 不稳定性、抽象性与距离 (I/A/D) | `max_instability: 0.8`, `max_abstractness: 0.8`, `max_distance: 0.5` |
| [E0024](/src/rules/examples/e24/e24.md) | 每个方法的代码行数 | `max_loc: 30` |
| [E0025](/src/rules/examples/e25/e25.md) | 每个文件的代码行数 | `max_loc: 500` |
| [E0026](/src/rules/examples/e26/e26.md) | 注释比率 | `min_ratio: 0.1`, `max_ratio: 0.5` |
| [E0027](/src/rules/examples/e27/e27.md) | 上帝类 (Brain Class) | `max_methods: 15`, `max_fields: 10` |
| [E0028](/src/rules/examples/e28/e28.md) | 数据类 | `max_getter_setter_ratio: 0.7`, `min_methods: 3` |
| [E0029](/src/rules/examples/e29/e29.md) | 扇入 / 扇出 | `max_fan_out: 10`, `max_fan_in: 20` |
| [E0030](/src/rules/examples/e30/e30.md) | 圈复杂度密度 | `max_density: 0.3` |
添加新规则非常简单 —— [这篇教程](./docs/adding_new_rule.md) 解释了具体方法。
### 文章
阅读发布在 [https://dev.to/denzyldick](https://dev.to/denzyldick) 上的一系列章节,以了解该项目的内部原理 —— 这是一份很棒且通俗易懂的入门读物。
1. [为 PHP 编写你自己的静态分析器。](https://dev.to/denzyldick/the-beginning-of-my-php-static-analyzer-in-rust-5bp8)
2. [我如何让编写面条式代码变得不可能。](https://dev.to/denzyldick/how-i-made-it-impossible-to-write-spaghetti-code-dg4)
3. [在 PHP 源代码的 AST 中检测面条式代码。](https://dev.to/denzyldick/traversing-an-ast-of-php-source-code-2kee)
4. [让 Symfony 应用适配 Swoole、RoadRunner 和 FrankenPHP(无 AI 介入)。](https://dev.to/sergiid/getting-symfony-app-ready-for-swoole-roadrunner-and-frankenphp-no-ai-involved-2d0g)
5. [改进你的 CI 输出](https://dev.to/denzyldick/improve-your-ci-output-2eg)
6. [为什么在 PHP 中使用 unserialize 是个坏主意](https://dev.to/denzyldick/why-is-unserializing-an-object-in-php-a-bad-idea-3odl)标签:OpenVAS, PHP, Rust, SOC Prime, 云安全监控, 代码规范, 可视化界面, 图数据库, 安全专业人员, 开发工具, 网络流量审计, 通知系统, 静态分析