Product Decisions This Supports
- Build vs. buy for refactor safety. When the roadmap includes a framework upgrade (Laravel major, PHP 8.4/8.5), a namespace reorganization, or a package swap, this answers "how do we know we didn't leave dangling references?" without a custom tooling epic. Cost is one dev dependency plus triage time — far below the engineering cost of hand-rolling an AST-based class checker.
- Quality-gate investment sequencing. Positions as layer zero of the static analysis stack: cheaper and faster than PHPStan/Psalm, narrower in scope. That's a budgeting decision — fund this first as a fast-failing sanity check, then justify the heavier tools on type/flow coverage, not on "does the class exist."
- Release readiness criteria. Gives the PM a concrete, verifiable pre-merge and pre-release checklist item ("no statically-visible dangling class references") that can be cited in a definition-of-done, rather than a verbal review convention.
- Prioritizing tech-debt remediation. The first report against a mature Laravel codebase is effectively a work inventory: stale service providers, dead facade aliases, orphaned policies and jobs, and stale PHPDoc. That list is directly prioritizable into cleanup sprints with visible before/after counts.
- Scope boundary decisions for the quality program. The tool's known blind spot — class names expressed as strings (config
providers/aliases, $app->make(), middleware aliases, morph maps) — forces an explicit product decision: do we accept that gap, or do we invest in a bespoke config-array checker? Documenting the gap is itself a decision that prevents false confidence in a green badge.
- Vendor-risk and support posture. MIT license, small surface, dev-only footprint, active changelog and PHP/PHPUnit support matrix mean low lock-in. That supports a "pilot then standardize" path rather than a procurement review, and a forked-fallback story if maintenance stalls.
When to Consider This Package
Adopt when:
- The codebase is large enough that manual class-reference review no longer works — typically dozens of service providers, jobs, policies, and form requests with regular refactoring.
- The team is mid- or post-refactor and wants a cheap regression net for renamed, removed, or mistyped classes.
- You're planning a PHP or PHPUnit major upgrade; the support matrix (v5.1 = PHP 8.1–8.5 / PHPUnit 10–13) is already aligned to current versions.
- You have, or are willing to assign, an owner for the ignore list and a review bar for adding entries.
- A green build being interpreted as "class references are fine" is acceptable because the team already knows the string-based blind spot and covers it elsewhere.
Look elsewhere or defer when:
- The team already runs PHPStan or Psalm at a level that catches unresolvable class references — marginal added value, doubled CI surface.
- The motivating incidents come from config arrays, container string bindings, or route middleware aliases — the tool is largely blind to these, so it won't move the metric it appears to target.
- There's no capacity for the first-run triage wave. Adopting without triage ownership produces a bloated permanent suppression list, which is worse than no gate.
- The codebase leans heavily on
class_alias(), dynamic/eval-generated classes, or optional integrations absent from the dev autoloader — false positives will dominate and erode trust in the gate.
- Your PHPUnit upgrade cadence is slower or faster than the maintainer's support matrix; version coupling means either you hold the upgrade or the gate breaks.
Before committing, get answers to: what share of recent class-reference incidents would this have caught; who owns getIgnored(); whether CI's dev autoloader includes all first-party packages and optional integrations; and how fast we could fork or drop it if maintenance stops.
How to Pitch It (Stakeholders)
For executives (one paragraph): Analyzer is a small, MIT-licensed developer tool that automatically catches references to classes that no longer exist — the kind of leftover from a rename or refactor that otherwise surfaces as a production error. It runs inside our existing test suite, adds nothing to production, and costs a single dev dependency. The main thing it buys us is confidence during framework and PHP upgrades, where stale references are a recurring, hard-to-review class of breakage. It is a low-cost, low-risk quality gate, not a platform commitment — and we can pilot it in report-only mode for a few weeks before it ever blocks a merge.
For engineering leadership: Treat it as layer zero of the static analysis stack — narrow and fast, sitting ahead of PHPStan/Psalm. It checks existence only, using nikic/php-parser, including PHPDoc references since v2.0. Zero runtime footprint, no service provider, no configuration, no container bindings — it's a PHPUnit trait, not an application dependency. Integration is a composer require --dev plus one test case declaring paths and ignores.
The honest framing — lead with it rather than let it be discovered later:
- Blind to string references. Config
providers/aliases, $app->make(), middleware aliases, and morph maps are invisible. That's a real gap in exactly some of the places Laravel breaks most.
- False positives will be the main operational cost, not installation. Budget two to four weeks report-only, classify every finding as defect / false-positive / accepted-suppression, then flip to blocking.
- Green does not mean "class references are correct" — only that statically-visible ones resolve.
- Version coupling is intentional: Analyzer majors track PHPUnit majors, so our PHPUnit upgrade cadence becomes an Analyzer upgrade.
- Adoption signals are unverified (no listed stars, 0 dependents). Decision rationale is a pilot with measurable outcomes, not social proof.
Ask: approve a time-boxed pilot (2–4 weeks, report-only, one named owner for triage and the ignore list) with a decision checkpoint on whether the findings justified promotion to a blocking gate.