halleck45/php-metrics
PhpMetrics analyzes PHP projects and classes, producing a readable HTML report packed with code metrics like cyclomatic complexity, maintainability index, and coupling. Install it as a dev dependency via Composer and run it against any folder to spot risky code and track quality over time.
PhpMetrics is a CLI application, not a library — you shell out to it and read its reports. Don't use Hal\... classes in your app; the Hal namespace is legacy, internal, and not covered by semver.
phpmetrics/phpmetrics, not the repo slug):
composer require phpmetrics/phpmetrics --dev
./vendor/bin/phpmetrics --help first. The online docs at phpmetrics.github.io/website and the repo's doc/ folder are the next stop; CLI flags have drifted across 2.x, so trust --help for your pinned version.php -d memory_limit=-1 ./vendor/bin/phpmetrics \
--report-html=build/phpmetrics \
--exclude=vendor --exclude=storage --exclude=node_modules --exclude=bootstrap/cache \
--composer=composer.json \
app/
build/phpmetrics/index.html.First use case: "Where is the rot?" Open the Explore tab, sort classes by Maintainability Index ascending and Cyclomatic Complexity descending, and take the top 10 as a refactoring backlog. The bubble chart (size × complexity × maintainability) gives the same answer visually within a minute of opening the report.
Commit a config file. Keep dev runs and CI identical instead of duplicating a dozen flags in three places:
# phpmetrics.yml
paths:
- app
exclude:
- vendor
- storage
- bootstrap/cache
- tests
report:
html: build/phpmetrics
json: build/phpmetrics.json
composer: true
Key names have changed between 2.x minors — verify against vendor/phpmetrics/phpmetrics/doc/ after every upgrade.
Composer scripts so nobody remembers the flags:
"scripts": {
"metrics": "@php -d memory_limit=-1 vendor/bin/phpmetrics --config=phpmetrics.yml",
"metrics:json": "@php -d memory_limit=-1 vendor/bin/phpmetrics --config=phpmetrics.yml --report-json=build/phpmetrics.json --quiet"
}
CI artifact pattern. Run on every PR (or nightly for big repos), publish build/phpmetrics/ as a build artifact. It's static HTML + JS, so it opens straight from a downloaded zip — no server needed.
Gate pattern. PhpMetrics is a report generator: do not assume it exits non-zero on bad metrics. Enforce thresholds yourself off the JSON:
php -d memory_limit=-1 vendor/bin/phpmetrics --config=phpmetrics.yml \
--report-json=build/metrics.json --quiet
jq 'keys' build/metrics.json # confirm shape once, per version
Then a small script (or jq) walks the per-file entries and fails if, say, maintainabilityIndex < 40 or cyclomaticComplexity > 30 for any class in app/. Run the same check on main and post the delta as a PR comment — trend is far more actionable than an absolute number.
Diff-scoped analysis for fast PR feedback: pass --git=<branch> to analyze only files changed since that branch. Cheap, but coupling metrics become meaningless on a partial graph — treat diff mode as a smoke test, and keep a full nightly run for the HTML report.
Feed other tools. --report-checkstyle, --report-gitlab, --report-junit, --report-sonar, --report-csv let PhpMetrics plug into existing dashboards instead of becoming yet another tab nobody opens. GitLab code-quality JSON is the highest-value one if you're on GitLab.
Laravel specifics: target app/, exclude storage/, bootstrap/cache/, database/migrations/, and any generated code. Facades, helper calls and Eloquent magic will inflate coupling/LCOM numbers — read those two metrics relative to your own baseline, not to a blog post's threshold.
-d memory_limit=-1 in CI; the default 128M dies on anything non-trivial. Excluding vendor is the single biggest speedup.--report-html takes a directory, not a file. If you later widen the target to ., you'll try to analyze your own report.--composer=composer.json (or composer: true), inheritance/interfaces from dependencies are invisible and coupling metrics undercount badly. This is the most common "why is my instability score weird" cause.--exclude is path-substring based, and repeatable — one flag per path. Windows users should mind separators when sharing a committed config.dot is installed; otherwise you get the SVG fallback or a blank panel. Install graphviz on CI runners if you publish reports.--report-json for automation and keep the HTML for the top-level folders.composer.lock and read the changelog when bumping.plugins: config key exists for git-style integrations, but writing custom metrics means patching internals. If you need bespoke rules, the sane pattern is: PhpMetrics emits JSON → your own script applies the policy. That keeps you off the Hal\ API entirely.How can I help you explore Laravel packages today?