Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Phpstan Drupal Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the extension and PHPStan as dev dependencies:

composer require --dev mglaman/phpstan-drupal phpstan/phpstan

If you also want Drupal deprecation reporting, add the deprecation rules:

composer require --dev phpstan/phpstan-deprecation-rules

Create or update phpstan.neon at the project root:

includes:
    - vendor/mglaman/phpstan-drupal/extension.neon
    - vendor/phpstan/phpstan-deprecation-rules/rules.neon

parameters:
    level: 1
    drupal_root: web
    paths:
        - web/modules/custom

drupal_root must point at the directory containing core/ and sites/, typically web in a Composer-based Drupal project. Run analysis with:

vendor/bin/phpstan analyse --memory-limit=1G

Look first at vendor/mglaman/phpstan-drupal/extension.neon to see exactly which rules and dynamic return-type extensions your installed version registers. The README’s drupal_root and configuration sections are the next stop.

A good first use case is running the extension against a single custom module at level 1. It immediately catches common Drupal mistakes: calls to removed or deprecated APIs, incorrect assumptions about \Drupal::service() return types, malformed plugin annotations, and hook implementations whose signatures no longer match the documented hook.

Implementation Patterns

Ratchet from a baseline. On an existing codebase, generate a baseline once and commit it:

vendor/bin/phpstan analyse --generate-baseline

Then add the baseline to phpstan.neon:

includes:
    - vendor/mglaman/phpstan-drupal/extension.neon
    - phpstan-baseline.neon

New code is held to the configured level while legacy noise stays suppressed. Periodically regenerate or shrink the baseline rather than letting it grow.

Use it in CI as a gate. A typical CI step:

vendor/bin/phpstan analyse \
    --no-progress \
    --error-format=github \
    --memory-limit=1G

Keep the Drupal root, Composer autoloader, and PHPStan config in the same checkout. Running from the project root avoids autoload and relative-path surprises.

Analyze one module while developing. Point paths at the module you are changing, then widen to all custom code for CI:

parameters:
    paths:
        - web/modules/custom/my_module

This keeps local feedback fast and avoids unrelated legacy errors.

Provide a bootstrap file for constants and globals. Drupal code often expects constants such as DRUPAL_ROOT. Use bootstrapFiles for lightweight definitions, not to manually boot the kernel:

parameters:
    bootstrapFiles:
        - phpstan-bootstrap.php
<?php

define('DRUPAL_ROOT', __DIR__ . '/web');

Keep the bootstrap silent. Any output can corrupt JSON or CI annotations.

Register custom entity types. Dynamic return types for entity storage and load() depend on knowing the entity class. For custom entities, add entityMapping:

parameters:
    entityMapping:
        my_entity:
            class: Drupal\my_module\Entity\MyEntity
            storage: Drupal\my_module\MyEntityStorage

Without this, \Drupal::entityTypeManager()->getStorage('my_entity')->load($id) may degrade to a generic object type.

Add project-specific rules. The extension gives you Drupal-aware infrastructure; custom architectural rules are inexpensive:

services:
    -
        class: App\PHPStan\Rules\NoDirectSuperglobalRule
        tags: [phpstan.rules.rule]

Use custom rules for team conventions such as forbidding \Drupal::service() in classes that should use constructor injection, or requiring accessCheck() on entity queries.

Use the deprecation workflow during upgrades. For Drupal major-version upgrades, include phpstan/phpstan-deprecation-rules and run analysis across custom modules. Treat deprecation errors as a migration queue. Filter by deprecation message or identifier in CI so you can track progress without failing every build immediately.

Wrap common commands in Composer scripts.

{
    "scripts": {
        "phpstan": "phpstan analyse --memory-limit=1G",
        "phpstan:baseline": "phpstan analyse --generate-baseline --memory-limit=1G"
    }
}

This keeps local and CI invocations aligned.

Gotchas and Tips

  • drupal_root is relative to the config file, not the shell’s current directory. If PHPStan reports that it cannot determine the Drupal root, check this first.
  • Do not point drupal_root at web/core. It must be the Drupal docroot containing core/ and sites/.
  • Bootstrapping can require a usable site. Some analysis paths need Drupal’s container or settings. In CI, either build a test site first or provide a minimal sites/default/settings.php; missing database credentials are a common cause of bootstrap failures.
  • Unregistered services lose their type. \Drupal::service('my_module.foo') resolves only when the service definition is discoverable. If it returns object, confirm the module is enabled or present in the analyzed codebase, or add a local @var annotation.
  • Dynamic return types do not replace good annotations. For complex entity or render-array code, add @var or @return annotations where PHPStan still reports mixed values.
  • Verify .module, .install, and .theme files are analyzed. The extension generally registers Drupal file extensions, but if files are skipped, inspect fileExtensions in your config and add the Drupal extensions explicitly.
  • Drupal arrays are often under-specified. Levels 5+ can produce a lot of missing iterable value type noise from core and contrib annotations. Ratchet levels gradually, and use treatPhpDocTypesAsCertain: false only if weak Drupal annotations are causing clear false positives.
  • Use targeted ignores with identifiers. Prefer @phpstan-ignore with a rule identifier or ignoreErrors with identifier over broad path-based ignores. Avoid setting reportUnmatchedIgnoredErrors: false permanently; it hides stale suppressions.
  • Clear the result cache after bootstrap or config changes. If behavior seems stale:
vendor/bin/phpstan clear-result-cache
  • Parallel analysis plus Drupal bootstrap can be memory-hungry. If workers crash, raise --memory-limit, reduce parallel.maximumNumberOfProcesses, or run with --debug locally to get a clearer stack trace. --debug disables parallelism.
  • Exclude generated and third-party code. Add excludePaths for generated PHP, compiled Twig, and vendored contrib you do not own. Analyze custom code, not the whole docroot.
  • Baselines go stale. A baseline that suppresses a fixed error will not fail by default, so review it during upgrades and after large refactors.
  • Keep PHPStan and the extension compatible. mglaman/phpstan-drupal tracks PHPStan and Drupal major versions; pin both in composer.json and upgrade them together.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package