|
55 | 55 | use function is_int; |
56 | 56 | use function is_string; |
57 | 57 | use function sprintf; |
| 58 | +use function str_contains; |
58 | 59 | use const ARRAY_FILTER_USE_BOTH; |
59 | 60 | use const ARRAY_FILTER_USE_KEY; |
60 | 61 | use const CURLOPT_SHARE; |
@@ -461,7 +462,18 @@ public static function selectFromArgs( |
461 | 462 | if (count($parametersAcceptors) === 1) { |
462 | 463 | $acceptor = $parametersAcceptors[0]; |
463 | 464 | if (!self::hasAcceptorTemplateOrLateResolvableType($acceptor)) { |
464 | | - return $acceptor; |
| 465 | + $skipEarlyReturn = false; |
| 466 | + if ($namedArgumentsVariants !== null && self::acceptorHasCompoundParameterNames($acceptor)) { |
| 467 | + foreach ($args as $arg) { |
| 468 | + if ($arg->name !== null) { |
| 469 | + $skipEarlyReturn = true; |
| 470 | + break; |
| 471 | + } |
| 472 | + } |
| 473 | + } |
| 474 | + if (!$skipEarlyReturn) { |
| 475 | + return $acceptor; |
| 476 | + } |
465 | 477 | } |
466 | 478 | } |
467 | 479 |
|
@@ -868,6 +880,142 @@ public static function combineAcceptors(array $acceptors): ExtendedParametersAcc |
868 | 880 | ); |
869 | 881 | } |
870 | 882 |
|
| 883 | + /** |
| 884 | + * @param ParametersAcceptor[] $acceptors |
| 885 | + */ |
| 886 | + public static function combineAcceptorsByParameterName(array $acceptors): ExtendedParametersAcceptor |
| 887 | + { |
| 888 | + if (count($acceptors) === 0) { |
| 889 | + throw new ShouldNotHappenException( |
| 890 | + 'getVariants() must return at least one variant.', |
| 891 | + ); |
| 892 | + } |
| 893 | + if (count($acceptors) === 1) { |
| 894 | + return self::wrapAcceptor($acceptors[0]); |
| 895 | + } |
| 896 | + |
| 897 | + $parametersByName = []; |
| 898 | + $parameterNames = []; |
| 899 | + foreach ($acceptors as $acceptor) { |
| 900 | + foreach ($acceptor->getParameters() as $parameter) { |
| 901 | + $name = $parameter->getName(); |
| 902 | + if (!isset($parametersByName[$name])) { |
| 903 | + $parameterNames[] = $name; |
| 904 | + $parametersByName[$name] = []; |
| 905 | + } |
| 906 | + $parametersByName[$name][] = $parameter; |
| 907 | + } |
| 908 | + } |
| 909 | + |
| 910 | + $acceptorCount = count($acceptors); |
| 911 | + $parameters = []; |
| 912 | + $isVariadic = false; |
| 913 | + $returnTypes = []; |
| 914 | + $phpDocReturnTypes = []; |
| 915 | + $nativeReturnTypes = []; |
| 916 | + |
| 917 | + foreach ($acceptors as $acceptor) { |
| 918 | + $returnTypes[] = $acceptor->getReturnType(); |
| 919 | + if ($acceptor instanceof ExtendedParametersAcceptor) { |
| 920 | + $phpDocReturnTypes[] = $acceptor->getPhpDocReturnType(); |
| 921 | + $nativeReturnTypes[] = $acceptor->getNativeReturnType(); |
| 922 | + } |
| 923 | + $isVariadic = $isVariadic || $acceptor->isVariadic(); |
| 924 | + } |
| 925 | + |
| 926 | + foreach ($parameterNames as $name) { |
| 927 | + $params = $parametersByName[$name]; |
| 928 | + $existsInAll = count($params) === $acceptorCount; |
| 929 | + |
| 930 | + $types = []; |
| 931 | + $nativeTypes = []; |
| 932 | + $phpDocTypes = []; |
| 933 | + $defaultValues = []; |
| 934 | + $paramIsVariadic = false; |
| 935 | + $outType = null; |
| 936 | + $closureThisType = null; |
| 937 | + $immediatelyInvokedCallable = TrinaryLogic::createMaybe(); |
| 938 | + $attributes = []; |
| 939 | + $isOptional = !$existsInAll; |
| 940 | + $passedByRef = $params[0]->passedByReference(); |
| 941 | + |
| 942 | + foreach ($params as $j => $param) { |
| 943 | + $types[] = $param->getType(); |
| 944 | + $paramIsVariadic = $paramIsVariadic || $param->isVariadic(); |
| 945 | + |
| 946 | + if (!$isOptional && $param->isOptional()) { |
| 947 | + $isOptional = true; |
| 948 | + } |
| 949 | + |
| 950 | + $defaultValue = $param->getDefaultValue(); |
| 951 | + if ($defaultValue !== null) { |
| 952 | + $defaultValues[] = $defaultValue; |
| 953 | + } |
| 954 | + |
| 955 | + if ($j > 0) { |
| 956 | + $passedByRef = $passedByRef->combine($param->passedByReference()); |
| 957 | + } |
| 958 | + |
| 959 | + if ($param instanceof ExtendedParameterReflection) { |
| 960 | + $nativeTypes[] = $param->getNativeType(); |
| 961 | + $phpDocTypes[] = $param->getPhpDocType(); |
| 962 | + $immediatelyInvokedCallable = $param->isImmediatelyInvokedCallable()->or($immediatelyInvokedCallable); |
| 963 | + $attributes = array_merge($attributes, $param->getAttributes()); |
| 964 | + |
| 965 | + if ($param->getOutType() !== null) { |
| 966 | + $outType = $outType === null ? $param->getOutType() : TypeCombinator::union($outType, $param->getOutType()); |
| 967 | + } else { |
| 968 | + $outType = null; |
| 969 | + } |
| 970 | + |
| 971 | + if ($param->getClosureThisType() !== null && $closureThisType !== null) { |
| 972 | + $closureThisType = TypeCombinator::union($closureThisType, $param->getClosureThisType()); |
| 973 | + } elseif ($closureThisType === null && $param === $params[0] && $param->getClosureThisType() !== null) { |
| 974 | + $closureThisType = $param->getClosureThisType(); |
| 975 | + } else { |
| 976 | + $closureThisType = null; |
| 977 | + } |
| 978 | + } else { |
| 979 | + $nativeTypes[] = new MixedType(); |
| 980 | + $phpDocTypes[] = $param->getType(); |
| 981 | + } |
| 982 | + } |
| 983 | + |
| 984 | + $combinedDefaultValue = count($defaultValues) === count($params) |
| 985 | + ? TypeCombinator::union(...$defaultValues) |
| 986 | + : null; |
| 987 | + |
| 988 | + $parameters[] = new ExtendedDummyParameter( |
| 989 | + $name, |
| 990 | + TypeCombinator::union(...$types), |
| 991 | + $isOptional, |
| 992 | + $passedByRef, |
| 993 | + $paramIsVariadic, |
| 994 | + $combinedDefaultValue, |
| 995 | + TypeCombinator::union(...$nativeTypes), |
| 996 | + TypeCombinator::union(...$phpDocTypes), |
| 997 | + $outType, |
| 998 | + $immediatelyInvokedCallable, |
| 999 | + $closureThisType, |
| 1000 | + $attributes, |
| 1001 | + ); |
| 1002 | + } |
| 1003 | + |
| 1004 | + $returnType = TypeCombinator::union(...$returnTypes); |
| 1005 | + $phpDocReturnType = $phpDocReturnTypes === [] ? null : TypeCombinator::union(...$phpDocReturnTypes); |
| 1006 | + $nativeReturnType = $nativeReturnTypes === [] ? null : TypeCombinator::union(...$nativeReturnTypes); |
| 1007 | + |
| 1008 | + return new ExtendedFunctionVariant( |
| 1009 | + TemplateTypeMap::createEmpty(), |
| 1010 | + null, |
| 1011 | + $parameters, |
| 1012 | + $isVariadic, |
| 1013 | + $returnType, |
| 1014 | + $phpDocReturnType ?? $returnType, |
| 1015 | + $nativeReturnType ?? new MixedType(), |
| 1016 | + ); |
| 1017 | + } |
| 1018 | + |
871 | 1019 | private static function wrapAcceptor(ParametersAcceptor $acceptor): ExtendedParametersAcceptor |
872 | 1020 | { |
873 | 1021 | if ($acceptor instanceof ExtendedParametersAcceptor) { |
@@ -907,6 +1055,16 @@ private static function wrapAcceptor(ParametersAcceptor $acceptor): ExtendedPara |
907 | 1055 | ); |
908 | 1056 | } |
909 | 1057 |
|
| 1058 | + private static function acceptorHasCompoundParameterNames(ParametersAcceptor $acceptor): bool |
| 1059 | + { |
| 1060 | + foreach ($acceptor->getParameters() as $parameter) { |
| 1061 | + if (str_contains($parameter->getName(), '|')) { |
| 1062 | + return true; |
| 1063 | + } |
| 1064 | + } |
| 1065 | + return false; |
| 1066 | + } |
| 1067 | + |
910 | 1068 | private static function wrapParameter(ParameterReflection $parameter): ExtendedParameterReflection |
911 | 1069 | { |
912 | 1070 | return $parameter instanceof ExtendedParameterReflection ? $parameter : new ExtendedDummyParameter( |
|
0 commit comments