mglaman/phpstan-drupal
PHPStan extension for Drupal that teaches static analysis about Drupal's APIs—hooks, services, plugins, entity types, and test base classes—so it catches real bugs and deprecations in modules, themes, and profiles while avoiding false positives.
Architecture fit. mglaman/phpstan-drupal is a PHPStan extension, not an application library. It plugs into the static-analysis layer of a codebase rather than the runtime, which makes it architecturally low-risk: it adds no production dependencies, no autoloaded code paths, and no runtime surface. The first thing to flag for any planning discussion is scope mismatch — the brief frames this as a Laravel/PHP package, but the extension is Drupal-specific. It teaches PHPStan about Drupal's service container, plugin managers, entity/field metadata, hook system, and test base classes. On a pure Laravel codebase it provides essentially zero value; Larastan is the comparable tool there. Value is realized only where the portfolio contains Drupal 9/10/11 sites or a migration off Drupal where deprecation detection shortens the exit.
Integration feasibility. High, for the target stack. Installation is a dev-only Composer require, a phpstan.neon include, and setting drupal_root to a bootstrapped site. The extension reflects into the Drupal kernel to resolve services and plugins, so the analysis environment must resemble a working install — this is the single biggest integration constraint and should be validated in a spike before committing. CI wiring is standard: a Composer script or Make target, a baseline file for legacy noise, and a non-zero exit code gate.
Technical risk. Moderate and concentrated in three areas: (1) bootstrap fragility — analysis fails or degrades when settings.php, the database, or contributed module discovery is unavailable in CI; (2) version coupling — the extension tracks PHPStan major versions and Drupal core minors, so an unpinned upgrade can break the gate overnight; (3) false positives on custom plugin annotations, @Service definitions with dynamic arguments, and test doubles, which erode developer trust faster than the tool earns it. Star count (208) understates adoption because this is the de facto standard for Drupal static analysis, but it also means a small maintainer base — bus factor is a real consideration for a gate you intend to make blocking.
Key questions. Which repositories are Drupal versus Laravel, and what percentage of the portfolio does that represent? Do CI runners have a database and installed site, or only a code checkout? Which PHPStan major version is already pinned elsewhere in the org, and does it match? Is there appetite for a blocking gate or only a reporting signal in the first quarter?
Stack fit. Clean fit for Drupal 9/10/11 on PHP 8.1+, running alongside drupal-check (which itself wraps this extension), drupal-rector, and phpcs with Drupal/DrupalPractice standards. It complements rather than overlaps those tools: PHPStan handles type and symbol correctness, PHPCS handles style, Rector handles automated fixing. On Laravel repositories, defer to Larastan and treat this package as out of scope.
Migration path. Add as a require-dev, publish a shared phpstan.neon.dist at the org level so every Drupal repo inherits identical rules, then introduce incrementally. Start at level 0–2 with reportUnmatchedIgnoredErrors: false, generate a baseline from the existing codebase, and ratchet the level upward per-repo only after the baseline stabilizes. Deprecation analysis is the highest-value early win for teams planning a Drupal 10→11 upgrade — it produces a concrete, prioritized worklist.
Compatibility. Pin the extension and PHPStan to compatible minor ranges in composer.json and renovate them deliberately rather than automatically. Verify Drupal core minor support in the extension's changelog before each core upgrade, and validate that contributed modules with unusual plugin definitions don't generate noise.
Sequencing. (1) One-repo spike proving CI bootstrap works without a full site install, or documenting the database requirement. (2) Shared config plus baseline on two representative repos. (3) Non-blocking CI reporting for one sprint to measure signal-to-noise. (4) Promote to blocking on new code paths only, then to full gate. (5) Roll out across the Drupal portfolio. Budget one to two sprints for steps 1–3 with a single engineer part-time.
Maintenance. Low code footprint, but non-zero ongoing cost: the config is shared infrastructure and needs an owner. Expect two to four upgrade events per year driven by PHPStan majors or Drupal core releases. The dependency is dev-only, so it never ships to production and cannot cause runtime incidents.
Support. Upstream is a single primary maintainer with a responsive but small contributor base. Internal support burden falls on whoever owns developer tooling; a documented "how to add an ignore rule" runbook is the highest-leverage artifact. Without it, developers will either disable the gate or paste blanket ignores, both of which destroy the tool's value.
Scaling. Analysis cost scales with codebase size and is memory-hungry because it reflects into the Drupal kernel. Large multisites may need --memory-limit tuning, parallelization via PHPStan's worker pool, and caching between CI runs. Runtime of a few minutes per repo is typical and acceptable; if it exceeds ten minutes, scope the gate to changed files first and run the full pass nightly.
Failure modes. Most likely: bootstrap failure in CI (missing settings, unreachable database) producing a red build unrelated to code quality — mitigate with a fail-fast preflight check. Second: a silent PHPStan major upgrade shifting rules and blocking unrelated PRs — mitigate with pinned versions and a scheduled upgrade ticket. Third: false-positive fatigue leading teams to baseline the whole repo, at which point the gate is decorative; track baseline size as a health metric. Fourth: extension lag on a new Drupal core release, forcing a temporary gate bypass — accept and document it.
Ramp-up. Engineers need roughly half a day to learn baseline generation, ignoreErrors syntax, and how to read Drupal-specific rule messages. The conceptual lift is small; the political lift of making a gate blocking is larger. Recommend a short internal demo and a two-week opt-out window before enforcement.
How can I help you explore Laravel packages today?