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