1: | <?php declare(strict_types = 1); |
2: | |
3: | namespace PHPStan\Reflection; |
4: | |
5: | use PHPStan\Type\ConditionalTypeForParameter; |
6: | use PHPStan\Type\ErrorType; |
7: | use PHPStan\Type\Generic\TemplateTypeMap; |
8: | use PHPStan\Type\Type; |
9: | use PHPStan\Type\TypeCombinator; |
10: | use function array_key_exists; |
11: | use function array_merge; |
12: | use function count; |
13: | use function is_int; |
14: | |
15: | class GenericParametersAcceptorResolver |
16: | { |
17: | |
18: | |
19: | |
20: | |
21: | |
22: | public static function resolve(array $argTypes, ParametersAcceptor $parametersAcceptor): ParametersAcceptor |
23: | { |
24: | $typeMap = TemplateTypeMap::createEmpty(); |
25: | $passedArgs = []; |
26: | |
27: | $parameters = $parametersAcceptor->getParameters(); |
28: | $namedArgTypes = []; |
29: | foreach ($argTypes as $i => $argType) { |
30: | if (is_int($i)) { |
31: | if (isset($parameters[$i])) { |
32: | $namedArgTypes[$parameters[$i]->getName()] = $argType; |
33: | continue; |
34: | } |
35: | if (count($parameters) > 0) { |
36: | $lastParameter = $parameters[count($parameters) - 1]; |
37: | if ($lastParameter->isVariadic()) { |
38: | $parameterName = $lastParameter->getName(); |
39: | if (array_key_exists($parameterName, $namedArgTypes)) { |
40: | $namedArgTypes[$parameterName] = TypeCombinator::union($namedArgTypes[$parameterName], $argType); |
41: | continue; |
42: | } |
43: | $namedArgTypes[$parameterName] = $argType; |
44: | } |
45: | } |
46: | continue; |
47: | } |
48: | |
49: | $namedArgTypes[$i] = $argType; |
50: | } |
51: | |
52: | foreach ($parametersAcceptor->getParameters() as $param) { |
53: | if (isset($namedArgTypes[$param->getName()])) { |
54: | $argType = $namedArgTypes[$param->getName()]; |
55: | } elseif ($param->getDefaultValue() !== null) { |
56: | $argType = $param->getDefaultValue(); |
57: | } else { |
58: | continue; |
59: | } |
60: | |
61: | $paramType = $param->getType(); |
62: | $typeMap = $typeMap->union($paramType->inferTemplateTypes($argType)); |
63: | $passedArgs['$' . $param->getName()] = $argType; |
64: | } |
65: | |
66: | $returnType = $parametersAcceptor->getReturnType(); |
67: | if ( |
68: | $returnType instanceof ConditionalTypeForParameter |
69: | && !$returnType->isNegated() |
70: | && array_key_exists($returnType->getParameterName(), $passedArgs) |
71: | ) { |
72: | $paramType = $returnType->getTarget(); |
73: | $argType = $passedArgs[$returnType->getParameterName()]; |
74: | $typeMap = $typeMap->union($paramType->inferTemplateTypes($argType)); |
75: | } |
76: | |
77: | $resolvedTemplateTypeMap = new TemplateTypeMap(array_merge( |
78: | $parametersAcceptor->getTemplateTypeMap()->map(static fn (string $name, Type $type): Type => new ErrorType())->getTypes(), |
79: | $typeMap->getTypes(), |
80: | )); |
81: | |
82: | return new ResolvedFunctionVariant( |
83: | $parametersAcceptor, |
84: | $resolvedTemplateTypeMap, |
85: | $passedArgs, |
86: | ); |
87: | } |
88: | |
89: | } |
90: | |