phpcpd-next аналізує ваш PHP-код на наявність дубльованих фрагментів. Він знаходить копіпаст, який легко пропустити під час рев'ю, але складно підтримувати в майбутньому.
Проєкт розвиває Лучано Федеріко Перейра як наступника архівованого інструменту phpcpd Себастьяна Бергманна. Він є повністю сумісною заміною (drop-in replacement) і використовує ту саму команду phpcpd.
Головне нововведення: інструмент розпізнає не лише ідентичні копії. Він помічає дублікати навіть тоді, коли рядки поміняли місцями або додали/видали окремі інструкції між однаковими блоками:
- Три рушії детекції — Rabin-Karp (точні збіги), TokenBag (змінений порядок) та опціональне суфіксне дерево (gapped Type-3) із додатковим
--fuzzyрежимом, що ігнорує назви змінних. - Чотири формати виводу — текст у консолі, PMD-CPD XML, JSON та SARIF 2.1.0 для інтеграції з GitHub Code Scanning.
- Headless API для запуску перевірки безпосередньо в коді, а також PHPUnit trait, який перетворює пошук дублікатів на частину тестів.
- Інструменти для CI — зрозумілі exit codes, кешування результатів та інкрементальна індексація змінених файлів.
- Пресети для фреймворків — зокрема для Laravel, з можливістю перевизначити налаштування через CLI.
- PHP 8.5+ без жодних сторонніх залежностей у Composer runtime та стабільні детерміновані результати.
# Три рушії, що працюють одночасно
Більшість подібних інструментів шукають лише повні копії. 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. Це дозволяє відображати знайдені копії прямо у вкладці Security на GitHub. Неточні збіги отримують статус 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
# Headless API та PHPUnit Assertions
Інструмент можна використовувати як бібліотеку через статичний виклик detect() — без консольних команд та файлів звітів:
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):
- 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. Інструмент встановлюється як dev-залежність:
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.