phpcpd-next 扫描您的 PHP 代码并报告从一处复制粘贴到另一处的代码块——这类重复在代码审查中容易被遗漏,后续同步维护时却会带来很大困扰。
该项目由 Luciano Federico Pereira 维护,作为 Sebastian Bergmann 的 phpcpd(已归档)的继任者,并保持与原有 phpcpd 命令的直接替换兼容。
其新增功能不仅能检测逐字复制,还能标记行顺序被重新排列、或在两个原本相同的代码块之间添加或删除了语句的重复:
- 三种检测引擎 — Rabin-Karp(精确匹配)、TokenBag(重排序匹配)和可选的后缀树(间隙 Type-3 匹配),并在此基础上提供对重命名不敏感的
--fuzzy匹配 - 四种输出格式 — 控制台文本、PMD-CPD XML、JSON 以及适用于 GitHub Code Scanning 的 SARIF 2.1.0
- 无头 API 支持进程内调用检测,同时提供一个 PHPUnit trait,可将重复检测转化为测试断言
- CI 特性 — 有意义的退出码、完整的结果缓存以及按文件的增量索引
- 框架预设 包含 Laravel,支持通过 CLI 标志覆盖预设默认值
- PHP 8.5+,零 Composer 运行时依赖,并产生确定性结果
三种引擎,默认同时运行
大多数复制/粘贴检测器仅能发现精确重复。phpcpd-next 默认每次运行时同时执行 Rabin-Karp(精确连续匹配)和 TokenBag(顺序无关的重叠检测,即使语句顺序被打乱仍能识别)。用于检测间隙克隆(在原本相同的代码块之间插入或删除了语句)的后缀树引擎为可选功能:
# 默认:精确 + 重排序检测
phpcpd src/
# 仅 Rabin-Karp(更快,无重排序检测)
phpcpd --rk src/
# 通过后缀树检测间隙 Type-3 克隆
phpcpd --algorithm=suffixtree src/
控制台输出会指出重复的范围,并建议进行重构,而非仅列出行号:
Found 2 code clones with 21 duplicated lines in 2 files:
- app/Services/Billing.php:12-33 (21 lines)
app/Services/Invoicing.php:40-61
→ Consider extracting the shared lines into a reusable method or constant.
37.50% duplicated lines out of 56 total lines of code.
SARIF 输出用于 GitHub Code Scanning
除了 PMD-CPD XML 和 JSON,phpcpd-next 还可输出 SARIF 2.1.0,使克隆在 GitHub Security 标签页中显示。不一致(已分化)的克隆映射为 warning 严重性,精确克隆映射为 note:
- name: Detect duplicated code
run: vendor/bin/phpcpd --log-sarif=phpcpd.sarif src/ || true
- name: Upload results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: phpcpd.sarif
无头 API 和 PHPUnit 断言
除 CLI 外,检测还可通过静态 detect() 调用在进程内运行——无需 shell 调用,也无需生成报告文件:
use LucianoPereira\PhpcpdNext\Phpcpd;
$clones = Phpcpd::detect(
paths: 'app',
minTokens: 60,
algorithm: null, // null = Rabin-Karp + TokenBag
preset: 'laravel',
);
foreach ($clones as $clone) {
echo $clone->numberOfLines(), " lines\n";
}
附带的 trait 可将其转化为测试,使重复检测成为一个回归检查,并在发现克隆位置时失败:
use LucianoPereira\PhpcpdNext\PHPUnit\AssertNoDuplication;
use PHPUnit\Framework\TestCase;
final class DuplicationTest extends TestCase
{
use AssertNoDuplication;
public function test_app_is_dry(): void
{
$this->assertNoDuplication(__DIR__ . '/../app', minTokens: 70);
}
}
适用于 CI 的增量缓存
对于大型代码库,--cache 会根据配置指纹和文件清单哈希存储结果,当文件未发生变化时重放缓存结果。--incremental 更进一步,仅对修改过的文件重新进行词法分析,并从每文件索引中复用其余部分(仅限 Rabin-Karp),输出类似 (incremental index: 412 reused, 3 scanned) 的摘要:
- uses: actions/cache@v4
with:
path: .phpcpd-cache
key: phpcpd-${{ hashFiles('**/*.php') }}
restore-keys: phpcpd-
- run: vendor/bin/phpcpd --incremental --cache-dir .phpcpd-cache src/
安装
该工具需要 PHP 8.5+、ext-dom 和 ext-mbstring,并作为开发依赖安装:
composer require --dev phpcpd-next/phpcpd
vendor/bin/phpcpd src/
Laravel 预设会扫描 app、routes、database 和 config,同时排除 vendor 代码、Blade 视图、迁移文件和 IDE-helper 文件:
vendor/bin/phpcpd --preset=laravel app/Services --min-tokens=60
您可以在 GitHub 上找到源代码和完整文档。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.