From f005a5c3c5c78d13c03f9864e2fd15675a27eb25 Mon Sep 17 00:00:00 2001 From: bjorn Date: Wed, 24 Jun 2026 10:18:37 +0200 Subject: [PATCH] Populate service map when bootstrap is skipped (e.g. under Rector) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The service map is built by DrupalAutoloader::register(), which runs as a bootstrapFiles entry. PHPStan executes those via CommandHelper. Tools that build the PHPStan container directly (Rector's PHPStanServicesFactory) never call CommandHelper, so the bootstrap is skipped and the map stays empty — \Drupal::service() then resolves to object instead of the concrete class. Register a guarded, eagerly-instantiated no-op return type extension that runs the autoloader against the live container when the map is still empty. A normal phpstan analyse run stays a no-op because the bootstrap already filled the map. Refs #995 --- extension.neon | 3 ++ src/Drupal/RectorServiceMapInitializer.php | 54 ++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 src/Drupal/RectorServiceMapInitializer.php diff --git a/extension.neon b/extension.neon index dfce4a75..9d820e66 100644 --- a/extension.neon +++ b/extension.neon @@ -262,6 +262,9 @@ parametersSchema: services: - class: mglaman\PHPStanDrupal\Drupal\ServiceMap + - + class: mglaman\PHPStanDrupal\Drupal\RectorServiceMapInitializer + tags: [phpstan.broker.dynamicStaticMethodReturnTypeExtension] - class: mglaman\PHPStanDrupal\Drupal\ExtensionMap - diff --git a/src/Drupal/RectorServiceMapInitializer.php b/src/Drupal/RectorServiceMapInitializer.php new file mode 100644 index 00000000..2c7daffc --- /dev/null +++ b/src/Drupal/RectorServiceMapInitializer.php @@ -0,0 +1,54 @@ +getServices() === []) { + (new DrupalAutoloader())->register($container); + } + } + + public function getClass(): string + { + return 'Drupal'; + } + + public function isStaticMethodSupported(MethodReflection $methodReflection): bool + { + return false; + } + + public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, Scope $scope): ?Type + { + return null; + } +}