jdlabails/php-project-analyzer-bundle
A PHP project analyzer bundle for inspecting application structure, dependencies, and code health. It helps developers understand complex codebases and identify areas for refactoring or maintenance, giving quick visibility into Laravel/PHP projects before audits, upgrades, or onboarding.
First, the framing: jdlabails/php-project-analyzer-bundle is a Symfony bundle, not a Laravel package. It ships no ServiceProvider, no auto-discovery, no Artisan command. You cannot composer require it into a Laravel app and expect it to wire itself up. Day-to-day, Laravel developers use this in one of two ways:
php-project-analyzer library/binary as a build-time tool against your Laravel source tree, invoked from Artisan or CI.Minimal steps for the sidecar path:
composer create-project symfony/skeleton:/tmp/analyzer # pin to a Symfony/FrameworkBundle
# version compatible with the bundle
cd /tmp/analyzer
composer require jdlabails/php-project-analyzer-bundle
Then:
config/bundles.php (Symfony Flex may not do this automatically given the package predates Flex conventions).bin/console config:dump-reference <alias> — the alias is not obviously php_project_analyzer, so dump the reference rather than guessing.app/, packages/, your own src/) and exclude vendor/, storage/, bootstrap/cache/, database/migrations/.bin/console list for the bundle's commands — they're typically prefixed with the bundle alias.Where to look first, in order:
README.md on the repo — it's thin, but it tells you the required Symfony/PHP versions.src/DependencyInjection/Configuration.php — the authoritative list of config keys and defaults.src/Controller/ and src/Resources/config/routing.* — tells you what the web UI exposes and at what path.vendor/jdlabails/php-project-analyzer (or similarly named) package — the actual analysis engine lives there; the bundle is just glue.First use case worth doing: run it once against app/ before a large refactor (e.g. extracting a domain layer), save the report, and use it as a trend baseline. Absolute numbers from this class of tool are noisy; deltas over time are what's actionable.
Pattern 1 — Artisan wrapper around the analyzer binary. Keeps the tool inside your team's normal workflow without pretending it's a Laravel package:
// app/Console/Commands/AnalyzeProject.php
public function handle()
{
$binary = base_path('vendor/bin/php-analyzer'); // or the path to your sidecar
$report = storage_path('app/analysis/report-'.now()->format('Ymd-His').'.json');
$process = new Process([
$binary,
'--source', base_path('app'),
'--exclude', 'vendor,storage,bootstrap/cache',
'--format', 'json',
'--output', $report,
]);
$process->setTimeout(600)->run();
if (! $process->isSuccessful()) {
$this->error($process->getErrorOutput());
return self::FAILURE;
}
$this->info("Report written to {$report}");
return self::SUCCESS;
}
Pattern 2 — Regression gate in CI. The highest-value use. Don't gate on absolute scores; gate on change vs. the last committed baseline.
# .github/workflows/quality.yml
- name: Analyze
run: php artisan analyze:project --output=build/analysis.json
- name: Compare to baseline
run: php scripts/compare-metrics.php build/analysis.json analysis/baseline.json
# fail if cyclomatic complexity or coupling regressed > 5%
Commit the baseline JSON; update it deliberately in refactor PRs so the diff is reviewable.
Pattern 3 — Scheduled drift tracking. Nightly job writes a JSON snapshot to a metrics table (or S3), then a small dashboard charts LOC, average complexity, and coupling per module. This is where the tool earns its keep — you see the module that quietly became the worst offender.
Pattern 4 — Scoped analysis per module. Run separate analyses for app/Domain, app/Http, and each packages/* directory so a hot spot in one module doesn't get averaged away by a large clean one. Parallelize with Process::start() if the runs are slow.
Pattern 5 — Sidecar service in docker-compose. Mount the Laravel repo read-only into a Symfony container that hosts the bundle's UI:
analyzer:
build: ./docker/analyzer # Symfony skeleton + bundle
volumes:
- ./:/src:ro
ports: ["127.0.0.1:8081:8000"] # loopback only — see Gotchas
environment:
ANALYZER_SOURCE: /src/app
Pattern 6 — Layer with tools you already trust. This bundle gives you cohesion/coupling/complexity heuristics. It is not a type checker or a dependency-architecture enforcer. Pair it with PHPStan/Larastan (correctness), Deptrac (layer boundaries), and Pint (style). Treat the analyzer as the "shape of the codebase" report, nothing more.
Pattern 7 — Custom analyzers (sidecar only). The bundle's extension point is Symfony's tag system: implement the engine's analyzer interface, register it as a service, and tag it with whatever tag the bundle uses (bin/console debug:container --tag=<tag> to discover it). You can add project-specific rules — e.g. "flag controllers over 200 LOC" — and they'll appear in the same report.
php:7.4-cli) and keep it out of your application's composer.json entirely.composer require this in a Laravel app. Dependency conflicts with Laravel's Symfony components (HttpFoundation, Console, Routing) are almost guaranteed, and you'll get no benefit since the bundle's wiring is kernel-level.__call/__get magic, Eloquent dynamic attributes, and container-resolved dependencies all look like "hidden coupling" or "unstable dependencies" to a static analyzer. Expect a noisy first report; calibrate by comparing Laravel module vs. Laravel module, not against absolute thresholds.php -n or XDEBUG_MODE=off before running.app/ trees plus generated code will blow the default 128M. Set memory_limit=-1 in the analyzer's PHP config, and always exclude vendor/ — analyzing vendor is how people conclude the tool is slow and useless.127.0.0.1, never expose it, and never deploy it to a shared/staging environment. If you want a hosted report, export JSON and render your own view.bin/console config:dump-reference (run from the sidecar app) prints the exact tree and defaults. Similarly, bin/console debug:router | grep -i analyz finds the UI route.templates/bundles/<BundleName>/... in the sidecar app. This is the cheapest customization point.phpmetrics --report-json plus a 50-line comparator script gives you 80% of the value with none of the maintenance risk — and the remaining 20% (cohesion metrics, coupling graphs) is available from PDepend, which is still maintained.How can I help you explore Laravel packages today?