Skip to content

Commit c6182c0

Browse files
committed
Dynamic return type for ContainerInterface|Controller::has()
1 parent 0cfea5c commit c6182c0

5 files changed

+43
-5
lines changed

Diff for: src/Type/Symfony/ContainerInterfaceDynamicReturnTypeExtension.php

+8-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public function getClass(): string
2727

2828
public function isMethodSupported(MethodReflection $methodReflection): bool
2929
{
30-
return $methodReflection->getName() === 'get';
30+
return in_array($methodReflection->getName(), ['get', 'has'], true);
3131
}
3232

3333
public function getTypeFromMethodCall(
@@ -36,7 +36,13 @@ public function getTypeFromMethodCall(
3636
Scope $scope
3737
): Type
3838
{
39-
return Helper::getTypeFromMethodCall($methodReflection, $methodCall, $scope, $this->serviceMap);
39+
switch ($methodReflection->getName()) {
40+
case 'get':
41+
return Helper::getGetTypeFromMethodCall($methodReflection, $methodCall, $scope, $this->serviceMap);
42+
case 'has':
43+
return Helper::getHasTypeFromMethodCall($methodReflection, $methodCall, $scope, $this->serviceMap);
44+
}
45+
throw new \PHPStan\ShouldNotHappenException();
4046
}
4147

4248
}

Diff for: src/Type/Symfony/ControllerDynamicReturnTypeExtension.php

+8-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public function getClass(): string
2727

2828
public function isMethodSupported(MethodReflection $methodReflection): bool
2929
{
30-
return $methodReflection->getName() === 'get';
30+
return in_array($methodReflection->getName(), ['get', 'has'], true);
3131
}
3232

3333
public function getTypeFromMethodCall(
@@ -36,7 +36,13 @@ public function getTypeFromMethodCall(
3636
Scope $scope
3737
): Type
3838
{
39-
return Helper::getTypeFromMethodCall($methodReflection, $methodCall, $scope, $this->serviceMap);
39+
switch ($methodReflection->getName()) {
40+
case 'get':
41+
return Helper::getGetTypeFromMethodCall($methodReflection, $methodCall, $scope, $this->serviceMap);
42+
case 'has':
43+
return Helper::getHasTypeFromMethodCall($methodReflection, $methodCall, $scope, $this->serviceMap);
44+
}
45+
throw new \PHPStan\ShouldNotHappenException();
4046
}
4147

4248
}

Diff for: src/Type/Symfony/Helper.php

+23-1
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
use PHPStan\Reflection\MethodReflection;
1313
use PHPStan\Reflection\ParametersAcceptorSelector;
1414
use PHPStan\Symfony\ServiceMap;
15+
use PHPStan\Type\Constant\ConstantBooleanType;
1516
use PHPStan\Type\ObjectType;
1617
use PHPStan\Type\Type;
1718
use PHPStan\Type\VerbosityLevel;
1819

1920
final class Helper
2021
{
2122

22-
public static function getTypeFromMethodCall(
23+
public static function getGetTypeFromMethodCall(
2324
MethodReflection $methodReflection,
2425
MethodCall $methodCall,
2526
Scope $scope,
@@ -42,6 +43,27 @@ public static function getTypeFromMethodCall(
4243
return $returnType;
4344
}
4445

46+
public static function getHasTypeFromMethodCall(
47+
MethodReflection $methodReflection,
48+
MethodCall $methodCall,
49+
Scope $scope,
50+
ServiceMap $serviceMap
51+
): Type
52+
{
53+
$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
54+
if (!isset($methodCall->args[0])) {
55+
return $returnType;
56+
}
57+
58+
$serviceId = ServiceMap::getServiceIdFromNode($methodCall->args[0]->value, $scope);
59+
if ($serviceId !== null) {
60+
$service = $serviceMap->getService($serviceId);
61+
return new ConstantBooleanType($service !== null && $service->isPublic());
62+
}
63+
64+
return $returnType;
65+
}
66+
4567
public static function specifyTypes(
4668
MethodReflection $methodReflection,
4769
MethodCall $node,

Diff for: tests/Type/Symfony/ContainerInterfaceDynamicReturnTypeExtensionTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public function getTypeFromMethodCallProvider(): array
7474
$parametersAcceptorFound = $this->createMock(ParametersAcceptor::class);
7575
$parametersAcceptorFound->expects(self::once())->method('getReturnType')->willReturn($foundType);
7676
$methodReflectionFound = $this->createMock(MethodReflection::class);
77+
$methodReflectionFound->expects(self::once())->method('getName')->willReturn('get');
7778
$methodReflectionFound->expects(self::once())->method('getVariants')->willReturn([$parametersAcceptorFound]);
7879
$scopeFound = $this->createMock(Scope::class);
7980
$scopeFound->expects(self::once())->method('getType')->willReturn(new ConstantStringType('withClass'));
@@ -82,6 +83,7 @@ public function getTypeFromMethodCallProvider(): array
8283
$parametersAcceptorNotFound = $this->createMock(ParametersAcceptor::class);
8384
$parametersAcceptorNotFound->expects(self::once())->method('getReturnType')->willReturn($notFoundType);
8485
$methodReflectionNotFound = $this->createMock(MethodReflection::class);
86+
$methodReflectionNotFound->expects(self::once())->method('getName')->willReturn('get');
8587
$methodReflectionNotFound->expects(self::once())->method('getVariants')->willReturn([$parametersAcceptorNotFound]);
8688
$scopeNotFound = $this->createMock(Scope::class);
8789
$scopeNotFound->expects(self::once())->method('getType')->willReturn(new ErrorType());

Diff for: tests/Type/Symfony/ControllerDynamicReturnTypeExtensionTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public function getTypeFromMethodCallProvider(): array
7474
$parametersAcceptorFound = $this->createMock(ParametersAcceptor::class);
7575
$parametersAcceptorFound->expects(self::once())->method('getReturnType')->willReturn($foundType);
7676
$methodReflectionFound = $this->createMock(MethodReflection::class);
77+
$methodReflectionFound->expects(self::once())->method('getName')->willReturn('get');
7778
$methodReflectionFound->expects(self::once())->method('getVariants')->willReturn([$parametersAcceptorFound]);
7879
$scopeFound = $this->createMock(Scope::class);
7980
$scopeFound->expects(self::once())->method('getType')->willReturn(new ConstantStringType('withClass'));
@@ -82,6 +83,7 @@ public function getTypeFromMethodCallProvider(): array
8283
$parametersAcceptorNotFound = $this->createMock(ParametersAcceptor::class);
8384
$parametersAcceptorNotFound->expects(self::once())->method('getReturnType')->willReturn($notFoundType);
8485
$methodReflectionNotFound = $this->createMock(MethodReflection::class);
86+
$methodReflectionNotFound->expects(self::once())->method('getName')->willReturn('get');
8587
$methodReflectionNotFound->expects(self::once())->method('getVariants')->willReturn([$parametersAcceptorNotFound]);
8688
$scopeNotFound = $this->createMock(Scope::class);
8789
$scopeNotFound->expects(self::once())->method('getType')->willReturn(new ErrorType());

0 commit comments

Comments
 (0)