Skip to content

Commit cf50248

Browse files
authored
Merge pull request #220 from brambaud/feat/support-class-resolver
Resolve return of ClassResolver::getInstanceFromDefinition
2 parents 8ebb84b + 78209f6 commit cf50248

13 files changed

+224
-15
lines changed

extension.neon

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ services:
4040
-
4141
class: mglaman\PHPStanDrupal\Type\ContainerDynamicReturnTypeExtension
4242
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
43+
-
44+
class: mglaman\PHPStanDrupal\Type\DrupalClassResolverDynamicReturnTypeExtension
45+
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
46+
-
47+
class: mglaman\PHPStanDrupal\Type\DrupalClassResolverDynamicStaticReturnTypeExtension
48+
tags: [phpstan.broker.dynamicStaticMethodReturnTypeExtension]
4349
-
4450
class: mglaman\PHPStanDrupal\Type\DrupalServiceDynamicReturnTypeExtension
4551
tags: [phpstan.broker.dynamicStaticMethodReturnTypeExtension]

phpstan.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ parameters:
1212
- drupal-autoloader.php
1313
- tests/src
1414
excludePaths:
15-
- tests/src/Type/data/entity-type-manager.php
15+
- tests/src/Type/data/*.php

src/Drupal/DrupalServiceDefinition.php

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

33
namespace mglaman\PHPStanDrupal\Drupal;
44

5+
use PHPStan\Type\ObjectType;
6+
use PHPStan\Type\StringType;
7+
use PHPStan\Type\Type;
8+
59
class DrupalServiceDefinition
610
{
711

@@ -95,4 +99,16 @@ public function getDeprecatedDescription(): string
9599
{
96100
return str_replace('%service_id%', $this->id, $this->deprecationTemplate ?? self::$defaultDeprecationTemplate);
97101
}
102+
103+
public function getType(): Type
104+
{
105+
// Work around Drupal misusing the SplString class for string
106+
// pseudo-services such as 'app.root'.
107+
// @see https://www.drupal.org/project/drupal/issues/3074585
108+
if ($this->getClass() === 'SplString') {
109+
return new StringType();
110+
}
111+
112+
return new ObjectType($this->getClass() ?? $this->id);
113+
}
98114
}

src/Type/ContainerDynamicReturnTypeExtension.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,7 @@ public function getTypeFromMethodCall(
6464
if ($methodReflection->getName() === 'get') {
6565
$service = $this->serviceMap->getService($serviceId);
6666
if ($service instanceof DrupalServiceDefinition) {
67-
// Work around Drupal misusing the SplString class for string
68-
// pseudo-services such as 'app.root'.
69-
// @see https://www.drupal.org/project/drupal/issues/3074585
70-
if ($service->getClass() === 'SplString') {
71-
return new StringType();
72-
}
73-
return new ObjectType($service->getClass() ?? $serviceId);
67+
return $service->getType();
7468
}
7569
return $returnType;
7670
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace mglaman\PHPStanDrupal\Type;
4+
5+
use Drupal\Core\DependencyInjection\ClassResolverInterface;
6+
use PhpParser\Node\Expr\MethodCall;
7+
use mglaman\PHPStanDrupal\Drupal\ServiceMap;
8+
use PHPStan\Analyser\Scope;
9+
use PHPStan\Reflection\MethodReflection;
10+
use PHPStan\Reflection\ParametersAcceptorSelector;
11+
use PHPStan\Type\DynamicMethodReturnTypeExtension;
12+
use PHPStan\Type\Type;
13+
14+
class DrupalClassResolverDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
15+
{
16+
/**
17+
* @var ServiceMap
18+
*/
19+
private $serviceMap;
20+
21+
public function __construct(ServiceMap $serviceMap)
22+
{
23+
$this->serviceMap = $serviceMap;
24+
}
25+
26+
public function getClass(): string
27+
{
28+
return ClassResolverInterface::class;
29+
}
30+
31+
public function isMethodSupported(MethodReflection $methodReflection): bool
32+
{
33+
return $methodReflection->getName() === 'getInstanceFromDefinition';
34+
}
35+
36+
public function getTypeFromMethodCall(
37+
MethodReflection $methodReflection,
38+
MethodCall $methodCall,
39+
Scope $scope
40+
): Type {
41+
if (0 === \count($methodCall->getArgs())) {
42+
return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
43+
}
44+
45+
return DrupalClassResolverReturnType::getType($methodReflection, $methodCall, $scope, $this->serviceMap);
46+
}
47+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace mglaman\PHPStanDrupal\Type;
4+
5+
use Drupal\Core\DependencyInjection\ClassResolverInterface;
6+
use mglaman\PHPStanDrupal\Drupal\ServiceMap;
7+
use PhpParser\Node\Expr\StaticCall;
8+
use PHPStan\Analyser\Scope;
9+
use PHPStan\Reflection\MethodReflection;
10+
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
11+
use PHPStan\Type\ObjectType;
12+
use PHPStan\Type\Type;
13+
14+
class DrupalClassResolverDynamicStaticReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension
15+
{
16+
/**
17+
* @var ServiceMap
18+
*/
19+
private $serviceMap;
20+
21+
public function __construct(ServiceMap $serviceMap)
22+
{
23+
$this->serviceMap = $serviceMap;
24+
}
25+
26+
public function getClass(): string
27+
{
28+
return \Drupal::class;
29+
}
30+
31+
public function isStaticMethodSupported(MethodReflection $methodReflection): bool
32+
{
33+
return $methodReflection->getName() === 'classResolver';
34+
}
35+
36+
public function getTypeFromStaticMethodCall(
37+
MethodReflection $methodReflection,
38+
StaticCall $methodCall,
39+
Scope $scope
40+
): Type {
41+
if (0 === \count($methodCall->getArgs())) {
42+
return new ObjectType(ClassResolverInterface::class);
43+
}
44+
45+
return DrupalClassResolverReturnType::getType($methodReflection, $methodCall, $scope, $this->serviceMap);
46+
}
47+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace mglaman\PHPStanDrupal\Type;
6+
7+
use mglaman\PHPStanDrupal\Drupal\DrupalServiceDefinition;
8+
use mglaman\PHPStanDrupal\Drupal\ServiceMap;
9+
use PhpParser\Node\Expr\CallLike;
10+
use PHPStan\Analyser\Scope;
11+
use PHPStan\Reflection\MethodReflection;
12+
use PHPStan\Reflection\ParametersAcceptorSelector;
13+
use PHPStan\Type\Constant\ConstantStringType;
14+
use PHPStan\Type\ObjectType;
15+
use PHPStan\Type\Type;
16+
17+
final class DrupalClassResolverReturnType
18+
{
19+
20+
public static function getType(
21+
MethodReflection $methodReflection,
22+
CallLike $methodCall,
23+
Scope $scope,
24+
ServiceMap $serviceMap
25+
): Type {
26+
$arg1 = $scope->getType($methodCall->getArgs()[0]->value);
27+
if (!$arg1 instanceof ConstantStringType) {
28+
return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
29+
}
30+
31+
$serviceDefinition = $serviceMap->getService($arg1->getValue());
32+
if ($serviceDefinition instanceof DrupalServiceDefinition) {
33+
return $serviceDefinition->getType();
34+
}
35+
36+
return new ObjectType($arg1->getValue());
37+
}
38+
}

src/Type/DrupalServiceDynamicReturnTypeExtension.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,7 @@ public function getTypeFromStaticMethodCall(
6262
$serviceId = $arg1->value;
6363
$service = $this->serviceMap->getService($serviceId);
6464
if ($service instanceof DrupalServiceDefinition) {
65-
// Work around Drupal misusing the SplString class for string
66-
// pseudo-services such as 'app.root'.
67-
// @see https://www.drupal.org/project/drupal/issues/3074585
68-
if ($service->getClass() === 'SplString') {
69-
return new StringType();
70-
}
71-
return new ObjectType($service->getClass() ?? $serviceId);
65+
return $service->getType();
7266
}
7367
return $returnType;
7468
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name: service_map
2+
type: module
3+
core_version_requirement: ^8 || ^9
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
services:
2+
service_map.my_service:
3+
class: Drupal\service_map\MyService

0 commit comments

Comments
 (0)