|
10 | 10 | use PHPStan\Analyser\Scope; |
11 | 11 | use PHPStan\DependencyInjection\AutowiredParameter; |
12 | 12 | use PHPStan\DependencyInjection\AutowiredService; |
| 13 | +use PHPStan\DependencyInjection\Type\DynamicParameterTypeExtensionProvider; |
13 | 14 | use PHPStan\Reflection\ConstantReflection; |
14 | 15 | use PHPStan\Reflection\ExtendedParameterReflection; |
| 16 | +use PHPStan\Reflection\FunctionReflection; |
| 17 | +use PHPStan\Reflection\MethodReflection; |
15 | 18 | use PHPStan\Reflection\ParameterReflection; |
16 | 19 | use PHPStan\Reflection\ParametersAcceptor; |
17 | 20 | use PHPStan\Reflection\ReflectionProvider; |
@@ -55,6 +58,7 @@ public function __construct( |
55 | 58 | private UnresolvableTypeHelper $unresolvableTypeHelper, |
56 | 59 | private PropertyReflectionFinder $propertyReflectionFinder, |
57 | 60 | private ReflectionProvider $reflectionProvider, |
| 61 | + private DynamicParameterTypeExtensionProvider $dynamicParameterTypeExtensionProvider, |
58 | 62 | #[AutowiredParameter(ref: '%checkFunctionArgumentTypes%')] |
59 | 63 | private bool $checkArgumentTypes, |
60 | 64 | #[AutowiredParameter] |
@@ -98,6 +102,7 @@ public function check( |
98 | 102 | string $exclusiveConstantsMessage, |
99 | 103 | string $bitmaskNotAllowedMessage, |
100 | 104 | ?array $renamedNamedArgumentParameterData, |
| 105 | + MethodReflection|FunctionReflection|null $calleeReflection = null, |
101 | 106 | ): array |
102 | 107 | { |
103 | 108 | if ($funcCall instanceof Node\Expr\MethodCall || $funcCall instanceof Node\Expr\StaticCall || $funcCall instanceof Node\Expr\FuncCall) { |
@@ -391,6 +396,13 @@ public function check( |
391 | 396 | if ($this->checkArgumentTypes) { |
392 | 397 | $parameterType = TypeUtils::resolveLateResolvableTypes($parameter->getType()); |
393 | 398 |
|
| 399 | + if (! $funcCall instanceof Node\Expr\New_) { |
| 400 | + $overriddenType = $this->getParameterTypeFromDynamicExtension($funcCall, $calleeReflection, $parameter, $scope); |
| 401 | + if ($overriddenType !== null) { |
| 402 | + $parameterType = $overriddenType; |
| 403 | + } |
| 404 | + } |
| 405 | + |
394 | 406 | if ( |
395 | 407 | !$parameter->passedByReference()->createsNewVariable() |
396 | 408 | || (!$isBuiltin && !$argumentValueType instanceof ErrorType) |
@@ -895,4 +907,50 @@ private function callReturnsByReference(Expr $expr, Scope $scope): bool |
895 | 907 | return false; |
896 | 908 | } |
897 | 909 |
|
| 910 | + private function getParameterTypeFromDynamicExtension( |
| 911 | + Node\Expr\FuncCall|Node\Expr\MethodCall|Node\Expr\StaticCall $funcCall, |
| 912 | + MethodReflection|FunctionReflection|null $calleeReflection, |
| 913 | + ParameterReflection $parameter, |
| 914 | + Scope $scope, |
| 915 | + ): ?Type |
| 916 | + { |
| 917 | + if ($calleeReflection === null) { |
| 918 | + return null; |
| 919 | + } |
| 920 | + |
| 921 | + if ($funcCall instanceof Node\Expr\FuncCall && $calleeReflection instanceof FunctionReflection) { |
| 922 | + foreach ($this->dynamicParameterTypeExtensionProvider->getDynamicFunctionParameterTypeExtensions() as $extension) { |
| 923 | + if (!$extension->isFunctionSupported($calleeReflection, $parameter)) { |
| 924 | + continue; |
| 925 | + } |
| 926 | + $type = $extension->getTypeFromFunctionCall($calleeReflection, $funcCall, $parameter, $scope); |
| 927 | + if ($type !== null) { |
| 928 | + return $type; |
| 929 | + } |
| 930 | + } |
| 931 | + } elseif ($funcCall instanceof Node\Expr\StaticCall && $calleeReflection instanceof MethodReflection) { |
| 932 | + foreach ($this->dynamicParameterTypeExtensionProvider->getDynamicStaticMethodParameterTypeExtensions() as $extension) { |
| 933 | + if (!$extension->isStaticMethodSupported($calleeReflection, $parameter)) { |
| 934 | + continue; |
| 935 | + } |
| 936 | + $type = $extension->getTypeFromStaticMethodCall($calleeReflection, $funcCall, $parameter, $scope); |
| 937 | + if ($type !== null) { |
| 938 | + return $type; |
| 939 | + } |
| 940 | + } |
| 941 | + } elseif ($funcCall instanceof Node\Expr\MethodCall && $calleeReflection instanceof MethodReflection) { |
| 942 | + foreach ($this->dynamicParameterTypeExtensionProvider->getDynamicMethodParameterTypeExtensions() as $extension) { |
| 943 | + if (!$extension->isMethodSupported($calleeReflection, $parameter)) { |
| 944 | + continue; |
| 945 | + } |
| 946 | + $type = $extension->getTypeFromMethodCall($calleeReflection, $funcCall, $parameter, $scope); |
| 947 | + if ($type !== null) { |
| 948 | + return $type; |
| 949 | + } |
| 950 | + } |
| 951 | + } |
| 952 | + |
| 953 | + return null; |
| 954 | + } |
| 955 | + |
898 | 956 | } |
0 commit comments