Skip to content

Commit 6a25edb

Browse files
mglamanclaude
andcommitted
Infer return type from class-string<T> argument in ClassResolver extensions
When a variable typed as class-string<T> is passed to ClassResolverInterface::getInstanceFromDefinition() or Drupal::classResolver(), PHPStan now infers the return type as T instead of falling back to object. The existing service-ID and constant-string class-name resolution via ServiceMap is unchanged. This only fills the gap for non-constant class-string<T> variables. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 2552230 commit 6a25edb

2 files changed

Lines changed: 22 additions & 0 deletions

File tree

src/Type/DrupalClassResolverReturnType.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ public static function getType(
2525
): Type {
2626
$arg1 = $scope->getType($methodCall->getArgs()[0]->value);
2727
if (count($arg1->getConstantStrings()) === 0) {
28+
// Handle class-string<T> typed variables
29+
$classStringObjectType = $arg1->getClassStringObjectType();
30+
if (count($classStringObjectType->getObjectClassNames()) > 0) {
31+
return $classStringObjectType;
32+
}
33+
2834
return ParametersAcceptorSelector::selectFromArgs(
2935
$scope,
3036
$methodCall->getArgs(),

tests/src/Type/data/drupal-class-resolver.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,22 @@
88

99
class Foo {}
1010

11+
function test_class_string_vars(): void {
12+
/** @var class-string<Foo> $fooClass */
13+
$fooClass = Foo::class;
14+
assertType(Foo::class, (new ClassResolver())->getInstanceFromDefinition($fooClass));
15+
assertType(Foo::class, \Drupal::service('class_resolver')->getInstanceFromDefinition($fooClass));
16+
assertType(Foo::class, \Drupal::classResolver()->getInstanceFromDefinition($fooClass));
17+
assertType(Foo::class, \Drupal::classResolver($fooClass));
18+
19+
/** @var class-string<MyService> $serviceClass */
20+
$serviceClass = MyService::class;
21+
assertType(MyService::class, (new ClassResolver())->getInstanceFromDefinition($serviceClass));
22+
assertType(MyService::class, \Drupal::service('class_resolver')->getInstanceFromDefinition($serviceClass));
23+
assertType(MyService::class, \Drupal::classResolver()->getInstanceFromDefinition($serviceClass));
24+
assertType(MyService::class, \Drupal::classResolver($serviceClass));
25+
}
26+
1127
function test(): void {
1228
assertType(Foo::class, (new ClassResolver())->getInstanceFromDefinition(Foo::class));
1329
assertType(Foo::class, \Drupal::service('class_resolver')->getInstanceFromDefinition(Foo::class));

0 commit comments

Comments
 (0)