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: * @api
20: * @param array<int|string, Type> $argTypes
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: