Skip to content

audit/2 api conventions - #1027

Draft
mglaman wants to merge 3 commits into
audit/1d-autoloader-cleanupfrom
audit/2-api-conventions
Draft

audit/2 api conventions#1027
mglaman wants to merge 3 commits into
audit/1d-autoloader-cleanupfrom
audit/2-api-conventions

Conversation

@mglaman

@mglaman mglaman commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Part 6 of 9 in the legacy-code audit stack (on top of #1036). Brings the oldest type extension, rules, and reflection classes in line with current PHPStan 2.x API conventions and the patterns the newer code in this repo already uses.

What changed

  • EntityTypeManagerGetStorageDynamicReturnTypeExtension now declares ?Type and returns null to fall back, computes the declared return type lazily instead of eagerly on every call, and resolves every constant string in a union — getStorage($cond ? 'node' : 'user') now infers NodeStorage|UserStorage instead of just NodeStorage. The Concat/MethodCall AST sniffing is gone; Scope::getType() already folds constant expressions. This file is the template for migrating the remaining eager-fallback extensions in a follow-up.
  • PluginManagerInspectionRule migrates from Node\Stmt\Class_ to InClassNode, which provides the ClassReflection directly. The ReflectionProvider constructor dependency, anonymous-class handling, and manual FQN round-trip all disappear. Behavior is unchanged.
  • GlobalDrupalDependencyInjectionRule resolves the called class via $scope->resolveName() and checks against the MethodReflection interface instead of ExtendedMethodReflection, which PHPStan marks @api-do-not-implement.
  • EntityFieldReflection guards ReflectionProvider::getClass() with hasClass(); EntityFieldsViaMagicReflectionExtension uses implementsInterface() instead of rebuilding an ObjectType from the class name on every property lookup; FieldItemListPropertyReflection builds nullable types with TypeCombinator::addNull().
  • Redundant ->line($node->getStartLine()) calls removed — that is already the default.

Testing

New type-inference fixtures cover the first-class callable (must not crash), union constant-string arguments, and the dynamic-ID fallback. Full suite, self-analysis, and phpcs are green.

🤖 Generated with Claude Code

@mglaman
mglaman force-pushed the audit/2-api-conventions branch from 11c49d2 to e8cd1dd Compare August 5, 2026 16:20
@mglaman
mglaman changed the base branch from audit/1-correctness-fixes to audit/1d-autoloader-cleanup August 5, 2026 16:44
@mglaman
mglaman force-pushed the audit/2-api-conventions branch from e8cd1dd to 99903cc Compare August 5, 2026 16:44
@mglaman
mglaman force-pushed the audit/2-api-conventions branch from 99903cc to 40887b7 Compare August 5, 2026 17:15
@mglaman
mglaman force-pushed the audit/2-api-conventions branch from 40887b7 to 6ae8385 Compare August 5, 2026 19:13
mglaman and others added 3 commits August 5, 2026 14:33
Return null to fall back to the declared return type instead of eagerly
computing it via ParametersAcceptorSelector, resolve every constant
string in a union instead of only the first, and drop the Concat and
MethodCall AST sniffing that the constant-strings check already
subsumes. Covers getStorage($cond ? 'node' : 'user') with a proper
union type now.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
InClassNode hands the rule its ClassReflection directly, which drops
the ReflectionProvider constructor dependency, the anonymous-class
namespacedName juggling, and the manual FQN round-trip through
ObjectType. Behavior is unchanged; the rule now simply never fires for
classes PHPStan cannot reflect.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- GlobalDrupalDependencyInjectionRule: resolve the called class through
  $scope->resolveName() instead of requiring a FullyQualified node, and
  type-check against the MethodReflection interface instead of
  ExtendedMethodReflection, which is marked api-do-not-implement.
- EntityFieldReflection: guard ReflectionProvider::getClass() with
  hasClass() so a missing Drupal interface degrades instead of throwing.
- EntityFieldsViaMagicReflectionExtension: check interfaces via
  ClassReflection::implementsInterface() instead of rebuilding an
  ObjectType from the class name, which discarded generics and
  allocated on every property lookup; drop the now-unused static
  helpers.
- FieldItemListPropertyReflection: build nullable types with
  TypeCombinator::addNull() instead of new UnionType.
- Drop ->line($node->getStartLine()) calls that restate the default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mglaman
mglaman force-pushed the audit/2-api-conventions branch from 6ae8385 to 1c6b5bd Compare August 5, 2026 19:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant