systemsdk/phpcpd
A maintained fork of phpcpd, the Copy/Paste Detector for PHP. It scans your codebase for duplicated code using tokenization, reports clones with file and line info, and supports configurable thresholds and CI integration to help keep projects clean.
What the package is: systemsdk/phpcpd is a maintained fork of the abandoned sebastian/phpcpd Copy/Paste Detector. It is a CLI-only, token-based duplication scanner for PHP that emits human-readable output and PMD-CPD XML, and returns a non-zero exit code when duplicate blocks exceed configured thresholds (--min-lines, --min-tokens, --fuzzy). It has no runtime role in a Laravel application.
Architecture fit: Excellent from a coupling standpoint. It installs as a require-dev Composer dependency, exposes a single binary (vendor/bin/phpcpd), and requires no service provider, config publish, facade, or application bootstrap. There is zero production surface area — the blast radius of adoption is limited to developer machines and CI runners. This is the lowest-risk category of dependency a Laravel team can take on.
Integration feasibility: High. composer require --dev systemsdk/phpcpd followed by vendor/bin/phpcpd app/ produces a usable baseline in minutes. The --log-pmd output is a stable, widely-consumed schema that SonarQube, Jenkins, and GitLab can ingest. Exit-code gating maps cleanly onto any CI system, and it composes with existing Composer scripts, Makefiles, or Laravel Pint/PHPStan jobs without conflict.
Technical risk: The risk is not architectural — it is lifecycle and supply chain. Signals are weak: 15 stars, a single-maintainer fork lineage, and a repository field reported as unknown, which prevents provenance verification. The upstream project was archived, so this fork inherits an aging tokenizer and detection engine. The real technical concern is PHP language drift: new syntax (readonly classes, enums, property hooks, asymmetric visibility) can cause parse failures or, worse, silent mis-tokenization that produces both false positives and false negatives. A recent release date is reassuring but must be corroborated against actual commit activity and a declared PHP version matrix.
Key questions for the maintainer/team:
sebastian/* dependencies, and do they conflict with PHPUnit or other dev tooling already pinned in the project?Verdict: Conditional adopt for a pilot. Architecturally safe, operationally cheap, but lifecycle risk is real — treat it as a replaceable utility, not a load-bearing quality gate.
Stack fit: Composer-native and framework-agnostic. Because it never loads inside the Laravel application, Laravel version compatibility is a non-issue; the only version constraint that matters is the PHP runtime on developer machines and CI images. It sits alongside Pint (style), PHPStan/Larastan (types), and PHPUnit (behavior) as a fourth, orthogonal quality dimension.
Migration path: If the team previously used sebastian/phpcpd, this is a near drop-in: same binary name, same CLI flags, same PMD-CPD XML schema. Migration is a one-line composer.json change plus a lockfile update, and any downstream PMD XML parser (SonarQube, Jenkins CPD plugin, custom dashboards) continues to work unchanged. If the team has no existing duplication tooling, migration cost is effectively zero — there is nothing to unwind.
Compatibility checks before merging: Confirm the package's PHP constraint against the CI image; run composer why-not / check for conflicts with PHPUnit's pinned sebastian/* versions; verify the binary runs on both the local dev PHP (likely 8.3/8.4 via Herd/Sail) and the CI PHP. Watch for the case where the fork's sebastian/finder-facade or tokenizer dependencies force a downgrade of an otherwise-current dev dependency tree.
Sequencing:
require-dev, pinned to an exact version, on a branch.app/ and tests/ only; record duplication percentage and the top offenders. Do not gate anything yet.--exclude for vendor/, storage/, bootstrap/cache/, database/migrations/, and generated code; adjust --min-lines/--min-tokens to suppress noise from DTOs, config arrays, and Eloquent relationship boilerplate.Maintenance: Low effort, moderate attention. Pin the exact version in composer.json and let Renovate/Dependabot propose bumps for human review rather than auto-merging, given the small maintainer base. Budget a recurring check (quarterly) on whether the fork is still alive; keep a documented fallback (jscpd with a PHP grammar, or Java-based PMD CPD in a sidecar container) so a stalled fork is an afternoon's substitution, not a project.
Support: Community-only. No commercial backing, no vendor SLA, no visible CVE intake process. Internal ownership should sit with whichever engineer owns static analysis; document the tool, the thresholds chosen and why, and the exclusion list so it does not become tribal knowledge.
Scaling: Duplication detection is comparative across token windows, so runtime grows faster than linearly with codebase size. On a large Laravel monolith this can move from seconds to several minutes. Mitigations: scope the scan to app/ and tests/ rather than the repo root, exclude generated and vendored trees aggressively, and in monorepos split into per-package jobs so a slow module does not serialize the whole pipeline. There is no meaningful incremental/caching mode, so plan for full scans in CI rather than diff-only runs.
Failure modes to design around:
1 for "duplicates found" versus a distinct failure for scan errors; CI wrappers must distinguish these or a broken tool will be mistaken for a clean repo.Ramp-up: Very low. Most developers need no training — the output is self-explanatory and the tool is not a pre-commit blocker by default. Realistic effort is roughly half a day for one engineer to wire the CI job, tune thresholds against the baseline, and write a short README section. Organization-wide comprehension cost is negligible.
How can I help you explore Laravel packages today?