bmitch/churn-php
churn-php helps you spot PHP files that are likely refactoring candidates by analyzing Git commit churn and cyclomatic complexity, then ranking files with a combined score and presenting the results in an easy-to-read table.
Installation:
composer require --dev bmitch/churn-php
or via Phive:
phive install churn
First Run:
vendor/bin/churn run src
This scans the src directory for PHP files, calculates churn scores (commit frequency + cyclomatic complexity), and displays results in a table.
Key Outputs:
File, Commits, Complexity, Score, Lines.churn.yml (if present) or CLI flags.--format json|csv|markdown|text (default: text).+-------------------------------------------+---------+------------+--------+--------+
| File | Commits | Complexity | Score | Lines |
+-------------------------------------------+---------+------------+--------+--------+
| src/Service/ComplexService.php | 42 | 18 | 0.95 | 250 |
| src/Controller/OldController.php | 12 | 30 | 0.87 | 180 |
+-------------------------------------------+---------+------------+--------+--------+
Pre-Refactoring:
vendor/bin/churn run src --min-score 0.5 --files-to-show 20
Score > 0.5 (adjust threshold as needed).Complexity and frequent Commits.CI Integration:
composer.json scripts:
"scripts": {
"churn": "churn run src --max-score-threshold 0.8 --format json > churn.json"
}
composer churn && if [ $(jq '.results[0].score' churn.json) -gt 0.8 ]; then exit 1; fi
Custom Outputs:
vendor/bin/churn run src --format markdown > REFACTORING.md
vendor/bin/churn run src --format csv > churn.csv
Ignoring Files:
churn.yml to exclude tests or vendors:
filesToIgnore:
- tests/
- vendor/
- src/Helpers/*.php
Laravel-Specific:
vendor/bin/churn run app/Http app/Console app/Providers
phpstan or psalm for deeper analysis:
vendor/bin/churn run src --format json | jq -r '.results[] | select(.score > 0.7) | .file' | xargs phpstan analyse
Parallel Processing:
--parallel-jobs (default: 10):
vendor/bin/churn run src --parallel-jobs 20
Version Control:
commitsSince for shorter/longer histories:
# churn.yml
commitsSince: "6 months ago"
git, mercurial, fossil, or subversion (default: git).False Positives:
Complexity but low Commits (e.g., legacy code) may not need refactoring.minScoreToShow to filter:
minScoreToShow: 0.3 # Only show files with score >= 0.3
Performance:
churn run app/Http app/Console.parallelJobs if OOM errors occur.VCS Limitations:
git fetch --unshallow.vcs: none for non-versioned projects (loses commit data).Hooks Not Triggering:
AfterFileAnalysisHook) may fail silently.hooks:
- /absolute/path/to/MyHook.php
AfterAnalysisHook, BeforeAnalysisHook.Cache Issues:
cachePath) may show outdated scores.cachePath: null to disable:
rm -f .churn.cache && vendor/bin/churn run src
Dry Runs:
vendor/bin/churn run app/Http --files-to-show 5
Verbose Output:
Complexity Calculation:
pdepend:
vendor/bin/pdepend --ignore=tests/ --short src/ | grep "Complexity"
Configuration Overrides:
churn.yml:
# Overrides --min-score 0.5 and --parallel-jobs 5
minScoreToShow: 0.5
parallelJobs: 5
Custom Hooks:
// src/Hooks/SlackHook.php
namespace App\Hooks;
use Churn\Event\Hook\AfterFileAnalysisHook;
use Churn\Event\Event\AfterFileAnalysis;
class SlackHook implements AfterFileAnalysisHook {
public function onAfterFileAnalysis(AfterFileAnalysis $event) {
if ($event->getScore() > 0.7) {
// Send Slack notification
}
}
}
Register in churn.yml:
hooks:
- App\Hooks\SlackHook
Output Format Extensions:
ResultsParser to add custom columns (e.g., "Last Modified").VCS Integration:
Churn\Vcs\VcsInterface.Complexity Metrics:
CyclomaticComplexityAssessor with a custom implementation using nikic/PHP-Parser for more granular control.How can I help you explore Laravel packages today?