phpstan-drupal 2.1.0 is out. The theme of this release: rules and behaviors that proved themselves as opt-ins are now the defaults. If you run `composer update` and see new errors, that is the release working as intended — everything below includes the configuration to opt back out.Nine rules are now enabled by defaultThese rules shipped as opt-ins over the 2.0 cycle. They have had time to bake, and they catch real bugs, so they no longer require configuration:
phpstan-drupal 2.1.0 is out. The theme of this release: rules and behaviors that proved themselves as opt-ins are now the defaults. If you run `composer update` and see new errors, that is the release working as intended — everything below includes the configuration to opt back out.
Nine rules are now enabled by defaultThese rules shipped as opt-ins over the 2.0 cycle. They have had time to bake, and they catch real bugs, so they no longer require configuration:
testClassSuffixNameRuledependencySerializationTraitPropertyRuleaccessResultConditionRulecacheableDependencyRulehookFormAlterRuleloggerFromFactoryPropertyAssignmentRuleentityStorageDirectInjectionRulesymfonyYamlParseRuleentityOperationsCacheabilityRuleDisable any of them individually if they don't fit your project:
parameters:
drupal:
rules:
cacheableDependencyRule: falseOne rename to watch for: hookRules is now hookFormAlterRule. If your phpstan.neon references the old key, PHPStan will reject the configuration until you rename it.
ContainerInterface::has() returns bool nowPreviously, $container->has('some.service') resolved to always-true for any service the service map knew about. PHPStan then flagged the guard as redundant:
if ($this->container->has('some.service')) {
// "Comparison will always evaluate to true" — so you delete the guard.
$this->container->get('some.service')->doThing();
}That guard is not redundant. Modules get uninstalled. Site builds differ. The static analyzer knowing about a service does not guarantee the runtime container has it. Deleting the check trades a static-analysis warning for a production crash.
This behavior shipped in bleedingEdge.neon first and has now graduated: has() returns bool for known services by default. If you want the old inference back:
parameters:
drupal:
bleedingEdge:
containerHasAlwaysTrue: trueA three-year-old one-line fix: LoadIncludesPR #587 from donquixote sat open since 2023. The LoadIncludes rule — which verifies $module_handler->loadInclude() points at a file that exists had its type check inverted:
// Before: bails on concrete ModuleHandler, fires on plain object/mixed.
if (!$type->isSuperTypeOf($moduleHandlerInterfaceType)->yes()) {
return [];
}
// After: fires only when the receiver is definitely a ModuleHandlerInterface.
if (!$moduleHandlerInterfaceType->isSuperTypeOf($type)->yes()) {
return [];
}The practical effect: code that type-hints the concrete ModuleHandler class was silently skipped for years. It gets checked now, so you may see new loadIncludes.* errors in code that never changed. Those are real findings, not regressions.
ClassResolverInterface::getInstanceFromDefinition(Foo::class) narrows to Foo. Usually right, but the class resolver checks the container first, and a service definition can substitute an entirely different class. With the narrowed type, this defensive assertion gets flagged as always-true:
$foo = $this->classResolver->getInstanceFromDefinition(Foo::class);
assert($foo instanceof Foo);The narrowing stays on by default because it is correct for the overwhelming majority of code. If you want your assertions to keep meaning something:
parameters:
drupal:
classResolverReturnType: falseSupporting 2.0.x2.1.0 introduces new errors by default. So 2.0 is not abandoned.
If you hit a bug on 2.0.x, open an issue and note the version. When you are ready to move to 2.1, the release notes walk through every behavior change with its opt-out.
Thanksdonquixote for the LoadIncludes fix, Niklan for the detailed report that shaped classResolverReturnType, and Fame Helsinki for sponsoring the project. If phpstan-drupal saves your team time, consider sponsoring.
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | DDEV Blog: DDEV July 2026: New Screencasts, Partner Perks | 5 | 7 | 16-07-2026 |
| 2 | Security advisories: Drupal core - Moderately critical - Cross-site scripting - SA-CORE-2026-011 | 0 | 7 | 15-07-2026 |
| 3 | Security advisories: Drupal core - Moderately critical - Cross-site scripting - SA-CORE-2026-012 | 0 | 5 | 15-07-2026 |
| 4 | Tag1 Insights: A New Direction for Authentication in Drupal Core | 0 | 5 | 15-07-2026 |
| 5 | BloomIdea: Dynamic Multibanco references in Drupal Commerce: Commerce ifthenpay 3.0 has arrived | 0 | 5 | 15-07-2026 |
| 6 | The Drop Times: Darren Oh Outlines Drupal Board Priorities on AI, Sovereignty, and Contributor Health | 0 | 5 | 16-07-2026 |
| 7 | Security advisories: Drupal core - Moderately critical - Information disclosure - SA-CORE-2026-010 | 0 | 5 | 15-07-2026 |
| 8 | devel/sbt - 2.0.2 | 0 | 5 | 13-07-2026 |
| 9 | devel/libbson - 2.3.1_1 | 0 | 5 | 13-07-2026 |