Languages / PHP

PHP and Laravel static analysis: dead code, complexity and call graphs

For type errors and unused code inside PHP, PHPStan and Psalm (with --find-dead-code) are the standard. repowise adds a Laravel-aware repository graph: composer PSR-4 autoload is resolved, route files link to controllers and middleware, and event listeners, policies, providers, scheduled jobs and artisan commands are edges, so framework wiring is not reported as dead.

Last reviewed: November 2026

Good tier

What repowise detects in PHP

Checked against the engine source. Every row runs locally with zero LLM calls.

repowise capabilities for PHP
Files parsed.php
Import resolutionFull. Composer PSR-4 map, classmap, literal require and include paths
Call graphResolved calls and inheritance
Dead codeUnreachable files and unused exports; dynamic calls lower confidence
Health markersComplexity, class metrics, Extract Method plans, assertion smells
Performance markersComplexity and class metrics feed the health score and Extract Method plans
Coverage reportsClover from PHPUnit, Cobertura, lcov
Frameworks recognisedLaravel routes to controllers and middleware, $listen, $subscribe, $policies, $commands, Event::listen, Gate::policy, providers, scheduled jobs, artisan commands; TYPO3 extension conventions

Specific to this language

How PHP code is read

Laravel wiring as edges

Route files link to controllers and middleware by alias. Event listeners and subscribers, policies, console commands and service providers link to their classes, including Event::listen, Gate::policy and withProviders. Event discovery in app/Listeners, scheduled jobs and artisan commands by signature are covered. Config, migrations and factories are anchored as entry points.

Dynamic PHP is a signal

class_exists, interface_exists, call_user_func, call_user_func_array, new $class and ReflectionClass near a file lower its confidence, so container-resolved and reflection-loaded code lands in the review tier.

Autoload first, then paths

PSR-4 namespaces resolve through composer.json, then the classmap, then literal require and include paths relative to the importer and the repository root. There is no fuzzy name fallback for PHP, which keeps guessed edges out.

Honest comparison

Native PHP tools, and when they are enough

Native PHP analysis tools
ToolWhat it covers
PHPStan (with Larastan for Laravel)Type-level static analysis with levels; Larastan teaches it Laravel's magic.
PsalmType analysis plus --find-dead-code for unused classes, methods and properties.
PHPMDUnused private code, cyclomatic complexity and other mess-detector rules.
composer-unusedComposer packages nothing uses.
DeptracArchitecture layer rules between namespaces.

Where repowise adds value

  • Laravel framework wiring in the graph, ranked findings with confidence and owners.
  • Health scores that combine complexity with churn and coverage, and hotspots from git history.
  • One graph with the Vue, React or Alpine frontend in the same repository.
  • Agent context over MCP: what calls a controller, which tests reach it, who owns it.

How to run it

Analyse a PHP repository

From the repository root. Agents get the same results through the get_dead_code, get_health and get_context MCP tools.

uv tool install repowise        # or: pipx install repowise
repowise init --yes --no-prose     # index the repo, no API key
repowise dead-code --min-confidence 0.7 --safe-only
repowise health --refactoring-targets
vendor/bin/phpunit --coverage-clover clover.xml
repowise coverage add clover.xml
repowise serve                     # graph, health and dead code in a local dashboard

Questions

What people ask before they try it.

How do I analyse a Laravel codebase?

Run repowise init in the project root. It resolves PSR-4 autoload, links routes to controllers and middleware and providers, events, policies and commands to their classes, then gives you the dependency map, health scores, dead code and git hotspots in one index.

Is it a replacement for PHPStan or Psalm?

They complement each other. PHPStan and Psalm are type checkers that run in CI. repowise works at repository level: graph, dead files, health, history and agent context.

Does it read PHPUnit coverage?

Yes. repowise coverage add clover.xml reads Clover from PHPUnit.

Does repowise send my PHP code to an LLM?

No. Parsing, the call graph, dead code, health scores, performance markers and git history all run locally with zero LLM calls. repowise init --yes --no-prose needs no API key, and the analysis itself makes no network calls. Written documentation prose is the one opt-in step that uses a model, and it can run on a local Ollama model.

Index a PHP repository.

Public repositories are free on repowise.dev. The CLI is open source and runs on your machine with no API key.