|
8 | 8 | use PHPStan\Analyser\Scope; |
9 | 9 | use PHPStan\DependencyInjection\AutowiredParameter; |
10 | 10 | use PHPStan\DependencyInjection\AutowiredService; |
| 11 | +use PHPStan\DependencyInjection\Type\DynamicParameterTypeExtensionProvider; |
11 | 12 | use PHPStan\Reflection\ExtendedParameterReflection; |
| 13 | +use PHPStan\Reflection\FunctionReflection; |
| 14 | +use PHPStan\Reflection\MethodReflection; |
12 | 15 | use PHPStan\Reflection\ParameterReflection; |
13 | 16 | use PHPStan\Reflection\ParametersAcceptor; |
14 | 17 | use PHPStan\Reflection\ReflectionProvider; |
@@ -49,6 +52,7 @@ public function __construct( |
49 | 52 | private UnresolvableTypeHelper $unresolvableTypeHelper, |
50 | 53 | private PropertyReflectionFinder $propertyReflectionFinder, |
51 | 54 | private ReflectionProvider $reflectionProvider, |
| 55 | + private DynamicParameterTypeExtensionProvider $dynamicParameterTypeExtensionProvider, |
52 | 56 | #[AutowiredParameter(ref: '%checkFunctionArgumentTypes%')] |
53 | 57 | private bool $checkArgumentTypes, |
54 | 58 | #[AutowiredParameter] |
@@ -87,6 +91,7 @@ public function check( |
87 | 91 | string $unresolvableReturnTypeMessage, |
88 | 92 | string $unresolvableParameterTypeMessage, |
89 | 93 | string $namedArgumentMessage, |
| 94 | + MethodReflection|FunctionReflection|null $calleeReflection = null, |
90 | 95 | ): array |
91 | 96 | { |
92 | 97 | if ($funcCall instanceof Node\Expr\MethodCall || $funcCall instanceof Node\Expr\StaticCall || $funcCall instanceof Node\Expr\FuncCall) { |
@@ -359,6 +364,13 @@ public function check( |
359 | 364 | if ($this->checkArgumentTypes) { |
360 | 365 | $parameterType = TypeUtils::resolveLateResolvableTypes($parameter->getType()); |
361 | 366 |
|
| 367 | + if (! $funcCall instanceof Node\Expr\New_) { |
| 368 | + $overriddenType = $this->getParameterTypeFromDynamicExtension($funcCall, $calleeReflection, $parameter, $scope); |
| 369 | + if ($overriddenType !== null) { |
| 370 | + $parameterType = $overriddenType; |
| 371 | + } |
| 372 | + } |
| 373 | + |
362 | 374 | if ( |
363 | 375 | !$parameter->passedByReference()->createsNewVariable() |
364 | 376 | || (!$isBuiltin && !$argumentValueType instanceof ErrorType) |
@@ -744,4 +756,50 @@ private function callReturnsByReference(Expr $expr, Scope $scope): bool |
744 | 756 | return false; |
745 | 757 | } |
746 | 758 |
|
| 759 | + private function getParameterTypeFromDynamicExtension( |
| 760 | + Node\Expr\FuncCall|Node\Expr\MethodCall|Node\Expr\StaticCall $funcCall, |
| 761 | + MethodReflection|FunctionReflection|null $calleeReflection, |
| 762 | + ParameterReflection $parameter, |
| 763 | + Scope $scope, |
| 764 | + ): ?Type |
| 765 | + { |
| 766 | + if ($calleeReflection === null) { |
| 767 | + return null; |
| 768 | + } |
| 769 | + |
| 770 | + if ($funcCall instanceof Node\Expr\FuncCall && $calleeReflection instanceof FunctionReflection) { |
| 771 | + foreach ($this->dynamicParameterTypeExtensionProvider->getDynamicFunctionParameterTypeExtensions() as $extension) { |
| 772 | + if (!$extension->isFunctionSupported($calleeReflection, $parameter)) { |
| 773 | + continue; |
| 774 | + } |
| 775 | + $type = $extension->getTypeFromFunctionCall($calleeReflection, $funcCall, $parameter, $scope); |
| 776 | + if ($type !== null) { |
| 777 | + return $type; |
| 778 | + } |
| 779 | + } |
| 780 | + } elseif ($funcCall instanceof Node\Expr\StaticCall && $calleeReflection instanceof MethodReflection) { |
| 781 | + foreach ($this->dynamicParameterTypeExtensionProvider->getDynamicStaticMethodParameterTypeExtensions() as $extension) { |
| 782 | + if (!$extension->isStaticMethodSupported($calleeReflection, $parameter)) { |
| 783 | + continue; |
| 784 | + } |
| 785 | + $type = $extension->getTypeFromStaticMethodCall($calleeReflection, $funcCall, $parameter, $scope); |
| 786 | + if ($type !== null) { |
| 787 | + return $type; |
| 788 | + } |
| 789 | + } |
| 790 | + } elseif ($funcCall instanceof Node\Expr\MethodCall && $calleeReflection instanceof MethodReflection) { |
| 791 | + foreach ($this->dynamicParameterTypeExtensionProvider->getDynamicMethodParameterTypeExtensions() as $extension) { |
| 792 | + if (!$extension->isMethodSupported($calleeReflection, $parameter)) { |
| 793 | + continue; |
| 794 | + } |
| 795 | + $type = $extension->getTypeFromMethodCall($calleeReflection, $funcCall, $parameter, $scope); |
| 796 | + if ($type !== null) { |
| 797 | + return $type; |
| 798 | + } |
| 799 | + } |
| 800 | + } |
| 801 | + |
| 802 | + return null; |
| 803 | + } |
| 804 | + |
747 | 805 | } |
0 commit comments