mglaman/phpstan-drupal
Composer 安装命令:
composer require mglaman/phpstan-drupal
包简介
Drupal extension and rules for PHPStan
README 文档
README
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.
Note
With Drupal 11.2, Drupal core is now using PHPStan 2.0. The 1.x branch of phpstan-drupal will be supported until Drupal 10 loses security support when Drupal 12 is released.
Sponsors
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
Default rules promoted in 2.1.0
As of 2.1.0, the following rules are enabled by default. Disable any of them individually if they don't fit your project:
parameters: drupal: rules: # Enforces that OOP hook implementations using the Hook attribute have the # correct method signature for hook_form_alter, hook_form_FORM_ID_alter, etc. # Requires Drupal 10.3+ (Hook attribute). hookFormAlterRule: false # Flags non-abstract test classes whose names do not end with "Test". testClassSuffixNameRule: false # Flags properties that are private or read-only in classes using # DependencySerializationTrait, which does not support them. dependencySerializationTraitPropertyRule: false # Flags calls to AccessResult static methods (::allowed(), ::forbidden(), etc.) # whose argument type already makes the condition always true or always false. accessResultConditionRule: false # Flags addCacheableDependency() calls whose argument does not implement # CacheableDependencyInterface. cacheableDependencyRule: false # Flags logger channel objects (from LoggerChannelFactoryInterface::get()) that # are assigned to a property in a class using DependencySerializationTrait, # which cannot serialize logger channels correctly. loggerFromFactoryPropertyAssignmentRule: false # Flags direct injection of EntityStorageInterface (or a subtype) into a # constructor. Inject EntityTypeManagerInterface and call getStorage() instead. entityStorageDirectInjectionRule: false # Flags direct use of Symfony\Component\Yaml\Yaml::parse() on Drupal-controlled # YAML files, which should go through Drupal's YAML parser. symfonyYamlParseRule: false # Flags cacheability issues in entity list builder and entity operation hooks. entityOperationsCacheabilityRule: false
Note
hookRules was renamed to hookFormAlterRule in 2.1.0 — update your configuration if you referenced it explicitly.
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
Disabling extensions
You can disable various extensions. This is useful when contributing to Drupal Core to improve its types.
parameters: drupal: extensions: entityFieldsViaMagicReflection: true entityFieldMethodsViaMagicReflection: true entityQuery: true entityRepository: true stubFiles: true
Both options are enabled by default.
Class resolver return types
ClassResolverInterface::getInstanceFromDefinition() and \Drupal::classResolver() calls are narrowed to the type of the passed class name or service ID. The return value is not guaranteed at runtime: the container is checked first, and a service definition can substitute a different class. Disable the narrowing to keep instanceof assertions meaningful:
parameters: drupal: classResolverReturnType: false
Enabled by default. When disabled, calls fall back to the declared object return type.
Bleeding-edge checks
bleedingEdge.neon enables hook deprecation checks against .api.php files and stricter service-container checking. New rules land here first before graduating to the default ruleset in a minor release.
includes: - vendor/mglaman/phpstan-drupal/bleedingEdge.neon
What it currently enables:
checkCoreDeprecatedHooksInApiFiles— reports hook implementations deprecated in Drupal core.api.phpfilescheckContribDeprecatedHooksInApiFiles— reports hook implementations deprecated in contrib module.api.phpfiles
Note
checkDeprecatedHooksInApiFiles is deprecated. Use checkCoreDeprecatedHooksInApiFiles and checkContribDeprecatedHooksInApiFiles instead.
Note
containerHasAlwaysTrue: false graduated from bleeding edge to the default in 2.1.0. ContainerInterface::has() returns bool instead of always-true for known services, so conditional service guards stay meaningful. Restore the old inference with:
parameters: drupal: bleedingEdge: containerHasAlwaysTrue: true
Config schema-based checks (experimental)
Two opt-in features use Drupal's config schema files to analyze Config::get() calls. Both only act on config objects whose schema has the FullyValidatable constraint, since only those schemas are guaranteed complete. Schema files are parsed lazily on first use, so there is no cost when the features are disabled.
parameters: drupal: # Narrows Config::get() return types from the config schema, e.g. # \Drupal::config('system.cron')->get('logging') resolves to bool|null # instead of mixed. Types are nullable because any key can be absent # at runtime. configGetReturnType: true rules: # Reports Config::get() calls with a key that does not exist in the # config schema, catching typos at analysis time. configGetUnknownKeyRule: true
Recognized call patterns:
\Drupal::config('...')->get('...')$configFactory->get('...')->get('...')$configFactory->getEditable('...')->get('...')$this->config('...')->get('...')in classes usingConfigFormBaseTrait
The config name and key must be literal strings. Config entity schemas with wildcard names (e.g. block.block.*) are not supported yet, and keys beneath dynamic type references (e.g. mailer_dsn.options.[%parent.scheme]) are not validated.
Neither feature is included in bleedingEdge.neon yet.
Detecting @todo comments referencing the current Drupal.org issue (contrib CI)
TodoCommentWithIssueUrlRule is an opt-in rule for Drupal contrib CI pipelines. When running PHPStan as part of a GitLab merge request, it reports an error for any @todo comment that contains a drupal.org issue URL matching the current issue — for example:
// @todo Remove once https://drupal.org/i/3456789 is resolved.
This prevents issue-specific TODOs from being accidentally merged without resolution.
The rule auto-detects the current issue NID from standard GitLab CI environment variables:
CI_MERGE_REQUEST_SOURCE_BRANCH_NAME(e.g.3456789-my-feature)CI_MERGE_REQUEST_SOURCE_PROJECT_PATH(e.g.issue/mymodule-3456789)
It is silent when neither variable is set, so it is safe to include in a shared config.
The rule is not registered by default. To enable it, add it to your project's phpstan.neon:
rules: - mglaman\PHPStanDrupal\Rules\Drupal\TodoCommentWithIssueUrlRule
Note
When using the Drupal GitLab CI templates,
adding extra rules requires a custom phpstan.neon that includes the default configuration, since adding additional
rules is not supported directly through the template variables.
Both drupal.org/i/{nid} and drupal.org/project/{project}/issues/{nid} URL formats are recognized.
Custom PHPDoc types
phpstan-drupal provides custom PHPDoc types that can be used to improve type safety in Drupal code.
entity-type-id
The entity-type-id type represents a valid Drupal entity type ID string (e.g. 'node', 'user', 'taxonomy_term'). PHPStan will report an error when a constant string that is not a known entity type ID is passed where entity-type-id is expected.
Drupal coding standards require keeping PHPStan-specific types in @phpstan-param and @phpstan-return tags rather than in the standard @param and @return tags:
/** * Loads an entity by its entity type ID and entity ID. * * @param string $entityTypeId * The entity type ID. * @param int|string $id * The entity ID. * * @phpstan-param entity-type-id $entityTypeId */ public function loadEntity(string $entityTypeId, int|string $id): ?EntityInterface { return $this->entityTypeManager->getStorage($entityTypeId)->load($id); }
For return types:
/** * Returns the entity type ID. * * @return string * The entity type ID. * * @phpstan-return entity-type-id */ public function getEntityTypeId(): string { return $this->entityTypeId; }
Known entity type IDs are sourced from the drupal.entityMapping parameter. See Entity storage mappings for how to register custom entity types so their IDs are also recognized.
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"
]
}
}
}
mglaman/phpstan-drupal 适用场景与选型建议
mglaman/phpstan-drupal 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 31.7M 次下载、GitHub Stars 达 207, 最近一次更新时间为 2018 年 12 月 18 日, 在 PHP 生态内属于活跃度较高的组件。
我们在过去多个企业项目中使用过 mglaman/phpstan-drupal 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 mglaman/phpstan-drupal 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
统计信息
- 总下载量: 31.7M
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 208
- 点击次数: 23
- 依赖项目数: 132
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2018-12-18