Analyzer¶
PHP alone doesn't tell Mago what a Drupal service, entity storage, config object or plugin is. The
worker adds three plugins to mago analyze that give the analyzer those types, and add checks for
mistakes that are specific to Drupal and to PHPUnit.
drupal, on by default: the types of services, entities, config and plugins, and the Drupal checks, among them the ports of phpstan-drupal's rules and of the DrupalPractice sniffs that need types.phpunit, on by default: the types of PHPUnit assertions, mocks and Prophecy doubles, and the checks for docblocks that type a double wrong. It depends on nothing specific to Drupal. See Tests.phpstan-ignores, off by default: lets@phpstan-ignorecomments drop the Mago issues that report the same finding. See PHPStan ignores.
With disable-default-plugins = true, list drupal and phpunit in [analyzer] plugins to keep
them.
The topic pages¶
Each topic page starts with the types for its topic, then has an entry for each check. An entry
gives the check's level, where the check comes from, the phpstan-drupal rule or Coder sniff ported by
the check, what it reports and what it leaves alone, and a collapsed note that compares it with
phpstan-drupal or Coder. Every code starts with drupal/ or phpunit/. The
table of checks below lists them all.
Two pages have types only:
- Core types and stubs: the core methods that are typed the way the code behaves, and the stub files;
- PHPStan ignores: the
phpstan-ignoresplugin.
Most checks skip test code, which is any file under a tests directory. The checks that look up
services, entity types, plugins and config also skip hook documentation in *.api.php files. Each
entry lists what its check skips.
To turn a check off, use Mago's own [analyzer] ignore, see
Turning rules off. How the worker finds the Drupal
root and how it caches what it reads are under
The worker.
phpstan-drupal rules¶
Five ports of phpstan-drupal rules are linter rules, not analyzer checks, since they need no types:
drupal/discouraged-function,
drupal/render-callback,
drupal/symfony-yaml-parse,
drupal/todo-current-issue and
drupal/test-class-suffix. The last one
reads the parent's name, and the analyzer check
drupal/test-class-name reports the misnamed test classes that
it can't see that way. The phpstan-drupal page maps every rule and
extension of phpstan-drupal, including the parts that aren't ported.
Checks¶
Every check, by page.
Services¶
The analyzer types what the service container returns, so a lookup such as
\Drupal::service('entity_type.manager') gets the class of its service. The checks report lookups
of unknown, private and deprecated services, and service definitions with the wrong number of
arguments.
| Check | Level | What it reports |
|---|---|---|
drupal/deprecated-service |
warning | A get(), \Drupal::service() or class resolver lookup of a service whose definition, or its parent:, has a deprecated: key. |
drupal/unknown-service |
warning | A get() or \Drupal::service() lookup of a string id that no services file or provider defines. |
drupal/service-argument-count |
error, or warning for extra arguments | A service definition with fewer arguments: than the constructor of its class requires. |
Dependency injection¶
Checks for classes that call \Drupal where they can take a service through dependency injection,
for entity storages that a class takes or keeps instead of asking the entity type manager, and for
the properties that DependencySerializationTrait can't restore.
| Check | Level | What it reports |
|---|---|---|
drupal/global-drupal-call |
warning | A call to a method of \Drupal in an instance method of a class that implements ContainerInjectionInterface or ContainerFactoryPluginInterface. |
drupal/static-entity-load |
warning | A static load() or loadMultiple() call on an entity class, such as Node::load(), in an instance method of a class that can take services. |
drupal/entity-storage-injection |
warning | A constructor parameter typed as an entity storage. |
drupal/entity-storage-property |
warning | A property whose declared type, or @var type, is an entity storage. |
drupal/logger-from-factory |
error | A get() call on a LoggerChannelFactoryInterface whose result is assigned straight to a property of $this, in the constructor of a class that uses DependencySerializationTrait. |
drupal/dependency-serialization-property |
error | A private property of a class that uses DependencySerializationTrait, the composing class included. |
Entities¶
The analyzer reads the entity types of the codebase, and types what the entity type manager, entity
handlers, entity queries and entity fields return. The checks report unknown entity type ids,
entity queries without an access check, uses of the deprecated original property, and entity type
definitions and list builders that leave out what core asks for.
| Check | Level | What it reports |
|---|---|---|
drupal/unknown-entity-type |
warning | A literal entity type id that no indexed entity type declares, passed to a getter of the entity type manager. |
drupal/entity-query-access-check |
error | execute() on an entity query whose chain doesn't have an accessCheck() call, unless the entity type is a known config entity type. |
drupal/deprecated-original |
warning | A read, a write, an isset() or an unset() of original on an entity. |
drupal/config-entity-export |
error | A config entity type in the entity type index without config_export. |
drupal/list-builder-cacheability |
error | A list builder's getOperations() or getDefaultOperations() method without a second parameter typed as CacheableMetadata, on core 11.3 and later 11.x releases. |
Config¶
The analyzer types config objects, and the values read from them by their config schema. The checks report config names without a schema, and keys that a fully validatable schema doesn't list.
| Check | Level | What it reports |
|---|---|---|
drupal/config-unknown-key |
error | A get('key') call on a config object with a known name, for a key that the fully validatable schema of that config doesn't list. |
drupal/config-unknown-name |
warning | A literal config name without a schema. |
Plugins¶
The analyzer types the plugins created by core's plugin managers and the definitions read by
plugins. The checks report unknown plugin ids, plugin managers that never call alterInfo() or
setCacheBackend(), and plugin annotations that still use the context key, which Drupal 9
removed.
| Check | Level | What it reports |
|---|---|---|
drupal/unknown-plugin |
warning | A createInstance() call with a literal plain id that no scanned plugin declares. |
drupal/plugin-manager-alter-info |
warning | A DefaultPluginManager subclass in the analyzed code whose own constructor never calls alterInfo(). |
drupal/plugin-manager-cache-backend |
warning | A DefaultPluginManager subclass in the analyzed code whose own constructor never calls setCacheBackend(). |
drupal/plugin-annotation-context |
error | A plugin annotation with a context key. |
Render arrays and forms¶
The analyzer accepts a response from a form's buildForm(), and checks the arguments that
getForm() passes on to it. The checks report callbacks in render and form arrays that can't be
called, trusted or serialized by core, and cacheable dependencies that can't have cacheability.
| Check | Level | What it reports |
|---|---|---|
drupal/unknown-callback |
error, or warning on #value_callback |
A callback that names a method its class doesn't have. |
drupal/non-static-callback |
error, or warning on #value_callback |
An array written as a static call that names an instance method. |
drupal/non-public-callback |
error, or warning on #value_callback |
A callback that names a protected or private method, a '::method' of a form included. |
drupal/untrusted-callback |
error | A callback under a render, date or component key that names a class method not trusted by core. |
drupal/form-closure-callback |
error | A closure, an arrow function or a first-class callable under a render or date key, written in a class that implements FormInterface. |
drupal/invalid-callback |
error | A value under a render, date or component key whose type PHP can never call: an int, a float, a bool, null, an array that can't be a two-element pair, or an object without __invoke(). |
drupal/trusted-callback-override |
warning | A method that overrides one with #[TrustedCallback], declared in a parent class or in a trait that a parent uses, when the override doesn't repeat the attribute. |
drupal/cacheable-dependency |
warning | An addCacheableDependency() call whose dependency is a scalar, an array, null, or a final class that doesn't implement CacheableDependencyInterface. |
Translation¶
Checks for text that users see but that can't be translated, such as a literal form error or a status message.
| Check | Level | What it reports |
|---|---|---|
drupal/untranslated-message |
warning | A message that starts with a string literal, passed to setErrorByName() or setError() of FormStateInterface, or to addMessage(), addStatus(), addWarning() or addError() of MessengerInterface. |
Hooks and modules¶
Checks for hook implementations, for the files that a module loads with loadInclude(), and for
classes that extend the internal classes of another module.
| Check | Level | What it reports |
|---|---|---|
drupal/deprecated-hook |
warning | An implementation of a hook whose hook_*() documentation function has a @deprecated tag. |
drupal/hook-form-alter-signature |
error | A form alter implementation whose first parameter isn't taken by reference. |
drupal/hook-entity-operation-cacheability |
error | An implementation of hook_entity_operation() whose second parameter isn't typed as CacheableMetadata. |
drupal/load-include |
error, or warning when the module is unknown | A loadInclude() call that names a file that doesn't exist in the module's directory. |
drupal/internal-class-extension |
warning | A class that extends an @internal class owned by another module. |
Deprecations¶
Where the analyzer leaves Drupal's deprecations alone, how to report only what is removed in the next Drupal major, and the checks for the deprecations not reported by Mago.
| Check | Level | What it reports |
|---|---|---|
drupal/deprecated-class |
warning | A class or an enum that implements a deprecated interface. |
drupal/deprecated-method |
warning | A call to a method that implements a deprecated interface method without saying so, an implementation from a trait included. |
drupal/implicit-transaction-commit |
warning | A startTransaction() call whose result is thrown away, which commits at once. |
Traits¶
Mago analyzes a trait on its own, without the classes that use it. The analyzer types the calls
from a trait to the methods of those classes, the properties of those classes that the trait
reads, and the fluent methods kept in traits by core. The
check reports a self:: or static:: call from a static method of a trait to an instance method.
| Check | Level | What it reports |
|---|---|---|
drupal/trait-non-static-call |
error | A self::name() or static::name() call to an instance method, in a static method of a trait. |
Tests¶
The analyzer types PHPUnit assertions, mocks and Prophecy doubles. The checks report assertions
that always pass, doubles that a type or a docblock gets wrong, and mistakes in Drupal's test
classes. The phpunit plugin does the PHPUnit and Prophecy work and depends on nothing in Drupal.
The drupal plugin has the checks for Drupal's test classes.
| Check | Level | What it reports |
|---|---|---|
phpunit/redundant-assertion |
warning | An assertion that the type of its value already guarantees, such as assertTrue(TRUE), or assertInstanceOf() on a value already typed as that class. |
phpunit/mock-call-on-plain-type |
warning | An expects() or method() call on a value whose type doesn't name a PHPUnit class or declare the method. |
phpunit/prophecy-union |
warning | A docblock type that unions ProphecyInterface or ObjectProphecy with another class. |
drupal/test-modules-visibility |
error | A public $modules property, static or not, typed or not, declared in a class that descends from PHPUnit's TestCase. |
drupal/browser-test-default-theme |
error | A browser test on a themeless profile that doesn't set $defaultTheme. |
drupal/component-test-core-base |
error | A class in the Drupal\Tests\Component namespace that extends UnitTestCase, KernelTestBase, BuildTestBase or BrowserTestBase, directly or through its parents. |
drupal/test-class-name |
error | A concrete class that descends from PHPUnit's TestCase, whose name doesn't end in Test, and that drupal/test-class-suffix doesn't take for a test. |