phpcpd-next: A Modern Copy/Paste Detector CLI for PHP 8.5+
Composer Pacakge PHP #PHP #Code Quality #CLI Tools #Laravel #Static Analysis #PHPUnit

phpcpd-next: A Modern Copy/Paste Detector CLI for PHP 8.5+

3 min read Mohamed Said Mohamed Said

phpcpd-next: A Modern Copy/Paste Detector for PHP 8.5+

Code duplication is one of those problems that sneaks past review and compounds quietly over time. phpcpd-next is a CLI tool that scans your PHP codebase and reports duplicated blocks — including cases that a simple text diff would miss.

Maintained by Luciano Federico Pereira as a successor to Sebastian Bergmann's archived phpcpd, it keeps the same phpcpd command so existing scripts and CI pipelines need no changes.

Three Detection Engines

Most copy/paste detectors only catch word-for-word duplicates. phpcpd-next ships three engines:

  • Rabin-Karp — exact contiguous matches, fast by default
  • TokenBag — order-invariant overlap, catches shuffled statements
  • Suffix tree — opt-in gapped Type-3 clones, where a statement was inserted or removed between otherwise identical blocks

Rabin-Karp and TokenBag run together on every default scan. The suffix-tree engine is opt-in:

# Default: exact + reordered detection
phpcpd src/

# Rabin-Karp only
phpcpd --rk src/

# Gapped clones via suffix tree
phpcpd --algorithm=suffixtree src/

Console output points at the duplicated ranges and suggests a refactor:

Found 2 code clones with 21 duplicated lines in 2 files:
  - app/Services/Billing.php:12-33 (21 lines)
    app/Services/Invoicing.php:40-61Consider extracting the shared lines into a reusable method or constant.

SARIF Output for GitHub Code Scanning

phpcpd-next writes four output formats: console text, PMD-CPD XML, JSON, and SARIF 2.1.0. The SARIF format integrates directly with GitHub Code Scanning, surfacing clones in the Security tab. Diverged clones map to warning severity; exact clones map to 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

Headless API and PHPUnit Assertions

Detection can run in-process via a static detect() call — no subprocess, no report files:

use LucianoPereira\PhpcpdNext\Phpcpd;

$clones = Phpcpd::detect(
    paths: 'app',
    minTokens: 60,
    algorithm: null, // Rabin-Karp + TokenBag
    preset: 'laravel',
);

A bundled PHPUnit trait turns duplication into a regression test:

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);
    }
}

Incremental Caching for CI

For larger codebases, --cache stores results keyed by a configuration fingerprint and file-manifest hash. --incremental goes further, re-tokenizing only changed files and reusing the rest from a per-file index (Rabin-Karp only):

- 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/

Laravel Preset and Installation

The tool requires PHP 8.5+, ext-dom, and ext-mbstring. Install it as a dev dependency:

composer require --dev phpcpd-next/phpcpd
vendor/bin/phpcpd src/

A built-in Laravel preset scans app, routes, database, and config while automatically excluding vendor code, Blade views, migrations, and IDE-helper files:

vendor/bin/phpcpd --preset=laravel app/Services --min-tokens=60

Key Takeaways

  • Drop-in replacement for the archived phpcpd with the same CLI command
  • Three engines: exact (Rabin-Karp), reordered (TokenBag), and gapped (suffix tree)
  • SARIF 2.1.0 output integrates with GitHub Code Scanning out of the box
  • PHPUnit trait makes duplication a first-class test assertion
  • Incremental indexing keeps CI scans fast on large codebases
  • Zero Composer runtime dependencies; requires PHP 8.5+
  • Built-in Laravel preset with sensible exclusions

Source: Laravel News — A Copy/Paste Detector CLI for PHP 8.5+

Found this useful?

Frequently Asked Questions

3 questions
Q01 What is the difference between phpcpd-next and the original phpcpd?
phpcpd-next is a maintained successor to Sebastian Bergmann's archived phpcpd. It keeps the same `phpcpd` command as a drop-in replacement but adds two additional detection engines (TokenBag for reordered clones and a suffix-tree engine for gapped clones), four output formats including SARIF 2.1.0, a headless PHP API, a PHPUnit assertion trait, incremental CI caching, and framework presets including Laravel.
Q02 How do I integrate phpcpd-next with GitHub Code Scanning?
Run phpcpd-next with the `--log-sarif` flag to produce a SARIF 2.1.0 file, then upload it using the `github/codeql-action/upload-sarif@v3` action. Diverged clones appear as warnings and exact clones as notes in the GitHub Security tab.
Q03 Does phpcpd-next slow down CI on large codebases?
No. The `--cache` flag stores results keyed by a configuration fingerprint and file-manifest hash, replaying cached results when nothing has changed. The `--incremental` flag goes further by re-tokenizing only modified files and reusing the per-file index for everything else, printing a summary such as `(incremental index: 412 reused, 3 scanned)`.

Continue reading

More Articles

View all