mglaman / phpstan-drupal

Extension for PHPStan to allow analysis of Drupal code.
https://phpstan-drupal.mglaman.dev/
MIT License
198 stars 76 forks source link
drupal hacktoberfest php phpstan static-analysis

phpstan-drupal

Tests CircleCI

Extension for PHPStan to allow analysis of Drupal code.

PHPStan is able to discover symbols by using autoloading provided by Composer. However, Drupal does not provide autoloading information for modules and themes. This project registers those namespaces so that PHPStan can properly discover symbols in your Drupal code base automatically.

Sponsors

undpaul Optasy Fame Helsinki

Would you like to sponsor?

Usage

When you are using phpstan/extension-installer, phpstan.neon will be automatically included.

Manual installation If you don't want to use `phpstan/extension-installer`, include `extension.neon` in your project's PHPStan config: ``` includes: - vendor/mglaman/phpstan-drupal/extension.neon ``` To include Drupal specific analysis rules, include this file: ``` includes: - vendor/mglaman/phpstan-drupal/rules.neon ```

Getting help

Ask for assistance in the discussions or #phpstan channel on Drupal Slack.

Excluding tests from analysis

To exclude tests from analysis, add the following parameter

parameters:
    excludePaths:
        - *Test.php
        - *TestBase.php

Deprecation testing

This project depends on phpstan/phpstan-deprecation-rules which adds deprecation rules. We provide Drupal-specific deprecated scope resolvers.

To only handle deprecation testing, use a phpstan.neon like this:

parameters:
    customRulesetUsed: true
    reportUnmatchedIgnoredErrors: false
    # Ignore phpstan-drupal extension's rules.
    ignoreErrors:
        - '#\Drupal calls should be avoided in classes, use dependency injection instead#'
        - '#Plugin definitions cannot be altered.#'
        - '#Missing cache backend declaration for performance.#'
        - '#Plugin manager has cache backend specified but does not declare cache tags.#'
includes:
    - vendor/mglaman/phpstan-drupal/extension.neon
    - vendor/phpstan/phpstan-deprecation-rules/rules.neon

To disable deprecation rules while using phpstan/extension-installer, you can do the following:

{
  "extra": {
    "phpstan/extension-installer": {
      "ignore": [
        "phpstan/phpstan-deprecation-rules"
      ]
    }
  }
}

See the extension-installer documentation for more information: https://github.com/phpstan/extension-installer#ignoring-a-particular-extension

Adapting to your project

Customizing rules

Disabling checks for extending @internal classes

You can disable the ClassExtendsInternalClassRule rule by adding the following to your phpstan.neon:

parameters: 
    drupal:
        rules:
            classExtendsInternalClassRule: false

Entity storage mappings.

The EntityTypeManagerGetStorageDynamicReturnTypeExtension service helps map dynamic return types. This inspects the passed entity type ID and tries to return a known storage class, besides the default EntityStorageInterface. The default mapping can be found in extension.neon. For example:

parameters:
    drupal:
        entityMapping:
            block:
                class: Drupal\block\Entity\Block
                storage: Drupal\Core\Config\Entity\ConfigEntityStorage
            node:
                class: Drupal\node\Entity\Node
                storage: Drupal\node\NodeStorage
            taxonomy_term:
                class: Drupal\taxonomy\Entity\Term
                storage: Drupal\taxonomy\TermStorage
            user:
                class: Drupal\user\Entity\User
                storage: Drupal\user\UserStorage

To add support for custom entities, you may add the same definition in your project's phpstan.neon. See the following example for adding a mapping for Search API:

parameters:
    drupal:
        entityMapping:
            search_api_index:
                class: Drupal\search_api\Entity\Index
                storage: Drupal\search_api\Entity\SearchApiConfigEntityStorage
            search_api_server:
                class: Drupal\search_api\Entity\Server
                storage: Drupal\search_api\Entity\SearchApiConfigEntityStorage              

Similarly, the EntityStorageDynamicReturnTypeExtension service helps to determine the type of the entity which is loaded, created etc.. when using an entity storage. For instance when using

$node = \Drupal::entityTypeManager()->getStorage('node')->create(['type' => 'page', 'title' => 'foo']);

It helps with knowing the type of the $node variable is Drupal\node\Entity\Node.

The default mapping can be found in extension.neon:

parameters:
    drupal:
        entityMapping:
            block:
                class: Drupal\block\Entity\Block
                storage: Drupal\Core\Config\Entity\ConfigEntityStorage
            node:
                class: Drupal\node\Entity\Node
                storage: Drupal\node\NodeStorage
            taxonomy_term:
                class: Drupal\taxonomy\Entity\Term
                storage: Drupal\taxonomy\TermStorage
            user:
                class: Drupal\user\Entity\User
                storage: Drupal\user\UserStorage

To add support for custom entities, you may add the same definition in your project's phpstan.neon likewise.

Providing entity type mappings for a contrib module

Contributed modules can provide their own mapping that can be automatically registered with a user's code base when they use the phpstan/extension-installer. The extension installer scans installed package's composer.json for a value in extra.phpstan. This will automatically bundle the defined include that contains an entity mapping configuration.

For example, the Paragraphs module could have the following entity_mapping.neon file:

parameters:
    drupal:
        entityMapping:
            paragraph:
                class: Drupal\paragraphs\Entity\Paragraph
            paragraphs_type:
                class: Drupal\paragraphs\Entity\ParagraphsType

Then in the composer.json for Paragraphs, the entity_mapping.neon would be provided as a PHPStan include

{
  "name": "drupal/paragraphs",
  "description": "Enables the creation of Paragraphs entities.",
  "type": "drupal-module",
  "license": "GPL-2.0-or-later",
  "require": {
    "drupal/entity_reference_revisions": "~1.3"
  },
  "extra": {
    "phpstan": {
      "includes": [
        "entity_mapping.neon"
      ]
    }
  }
}