1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Reflection;
4:
5: use Closure;
6: use PhpParser\Node;
7: use PHPStan\Analyser\ArgumentsNormalizer;
8: use PHPStan\Analyser\MutatingScope;
9: use PHPStan\Analyser\Scope;
10: use PHPStan\Node\Expr\ParameterVariableOriginalValueExpr;
11: use PHPStan\Parser\ArrayFilterArgVisitor;
12: use PHPStan\Parser\ArrayFindArgVisitor;
13: use PHPStan\Parser\ArrayMapArgVisitor;
14: use PHPStan\Parser\ArrayWalkArgVisitor;
15: use PHPStan\Parser\ClosureBindArgVisitor;
16: use PHPStan\Parser\ClosureBindToVarVisitor;
17: use PHPStan\Parser\CurlSetOptArgVisitor;
18: use PHPStan\Parser\CurlSetOptArrayArgVisitor;
19: use PHPStan\Parser\ImplodeArgVisitor;
20: use PHPStan\Reflection\Callables\CallableParametersAcceptor;
21: use PHPStan\Reflection\Native\NativeParameterReflection;
22: use PHPStan\Reflection\Php\DummyParameter;
23: use PHPStan\Reflection\Php\ExtendedDummyParameter;
24: use PHPStan\ShouldNotHappenException;
25: use PHPStan\TrinaryLogic;
26: use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
27: use PHPStan\Type\ArrayType;
28: use PHPStan\Type\BooleanType;
29: use PHPStan\Type\CallableType;
30: use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
31: use PHPStan\Type\Constant\ConstantIntegerType;
32: use PHPStan\Type\Generic\TemplateTypeMap;
33: use PHPStan\Type\Generic\TemplateTypeVarianceMap;
34: use PHPStan\Type\IntegerType;
35: use PHPStan\Type\IntersectionType;
36: use PHPStan\Type\MixedType;
37: use PHPStan\Type\NullType;
38: use PHPStan\Type\ObjectType;
39: use PHPStan\Type\ResourceType;
40: use PHPStan\Type\StringType;
41: use PHPStan\Type\Type;
42: use PHPStan\Type\TypeCombinator;
43: use PHPStan\Type\UnionType;
44: use function array_is_list;
45: use function array_key_exists;
46: use function array_key_last;
47: use function array_last;
48: use function array_map;
49: use function array_merge;
50: use function array_slice;
51: use function array_values;
52: use function constant;
53: use function count;
54: use function defined;
55: use function is_int;
56: use function is_string;
57: use function sprintf;
58: use const ARRAY_FILTER_USE_BOTH;
59: use const ARRAY_FILTER_USE_KEY;
60: use const CURLOPT_SHARE;
61: use const CURLOPT_SSL_VERIFYHOST;
62:
63: /**
64: * @api
65: */
66: final class ParametersAcceptorSelector
67: {
68:
69: /**
70: * @param Node\Arg[] $args
71: * @param ParametersAcceptor[] $parametersAcceptors
72: * @param ParametersAcceptor[]|null $namedArgumentsVariants
73: */
74: public static function selectFromArgs(
75: Scope $scope,
76: array $args,
77: array $parametersAcceptors,
78: ?array $namedArgumentsVariants = null,
79: ): ParametersAcceptor
80: {
81: $types = [];
82: $unpack = false;
83: if (
84: count($args) > 0
85: && count($parametersAcceptors) > 0
86: ) {
87: $arrayMapArgs = $args[0]->value->getAttribute(ArrayMapArgVisitor::ATTRIBUTE_NAME);
88: if ($arrayMapArgs !== null) {
89: $callbackParameters = [];
90: $nativeCallbackParameters = [];
91: foreach ($arrayMapArgs as $arg) {
92: $argType = $scope->getType($arg->value);
93: $nativeArgType = $scope->getNativeType($arg->value);
94: if ($arg->unpack) {
95: $constantArrays = $argType->getConstantArrays();
96: if (count($constantArrays) > 0) {
97: foreach ($constantArrays as $constantArray) {
98: $valueTypes = $constantArray->getValueTypes();
99: foreach ($valueTypes as $valueType) {
100: $callbackParameters[] = new DummyParameter('item', $scope->getIterableValueType($valueType), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null);
101: }
102: }
103: }
104: $nativeConstantArrays = $nativeArgType->getConstantArrays();
105: if (count($nativeConstantArrays) > 0) {
106: foreach ($nativeConstantArrays as $constantArray) {
107: $valueTypes = $constantArray->getValueTypes();
108: foreach ($valueTypes as $valueType) {
109: $nativeCallbackParameters[] = new DummyParameter('item', $scope->getIterableValueType($valueType), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null);
110: }
111: }
112: }
113: } else {
114: $callbackParameters[] = new DummyParameter('item', $scope->getIterableValueType($argType), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null);
115: $nativeCallbackParameters[] = new DummyParameter('item', $scope->getIterableValueType($nativeArgType), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null);
116: }
117: }
118:
119: $acceptor = $parametersAcceptors[0];
120: $parameters = $acceptor->getParameters();
121: if (isset($parameters[0])) {
122: $callableType = new UnionType([
123: new CallableType($callbackParameters, new MixedType(), false),
124: new NullType(),
125: ]);
126: $nativeCallableType = new UnionType([
127: new CallableType($nativeCallbackParameters, new MixedType(), false),
128: new NullType(),
129: ]);
130: $parameters[0] = self::overrideParameterType($parameters[0], $callableType, $nativeCallableType);
131: $parametersAcceptors = [self::overrideAcceptorParameters($acceptor, $parameters)];
132: }
133: }
134:
135: if (count($args) >= 3 && (bool) $args[0]->getAttribute(CurlSetOptArgVisitor::ATTRIBUTE_NAME)) {
136: $optType = $scope->getType($args[1]->value);
137:
138: $valueTypes = [];
139: foreach ($optType->getConstantScalarValues() as $scalarValue) {
140: if (!is_int($scalarValue)) {
141: $valueTypes = [];
142: break;
143: }
144:
145: $valueType = self::getCurlOptValueType($scalarValue);
146: if ($valueType === null) {
147: $valueTypes = [];
148: break;
149: }
150:
151: $valueTypes[] = $valueType;
152: }
153:
154: $acceptor = $parametersAcceptors[0];
155: $parameters = $acceptor->getParameters();
156: if (count($valueTypes) !== 0 && isset($parameters[2])) {
157: $parameters[2] = new NativeParameterReflection(
158: $parameters[2]->getName(),
159: $parameters[2]->isOptional(),
160: TypeCombinator::union(...$valueTypes),
161: $parameters[2]->passedByReference(),
162: $parameters[2]->isVariadic(),
163: $parameters[2]->getDefaultValue(),
164: );
165:
166: $parametersAcceptors = [
167: new FunctionVariant(
168: $acceptor->getTemplateTypeMap(),
169: $acceptor->getResolvedTemplateTypeMap(),
170: $parameters,
171: $acceptor->isVariadic(),
172: $acceptor->getReturnType(),
173: $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(),
174: ),
175: ];
176: }
177: }
178:
179: if (count($args) >= 2 && (bool) $args[1]->getAttribute(CurlSetOptArrayArgVisitor::ATTRIBUTE_NAME)) {
180: $optArrayType = $scope->getType($args[1]->value);
181:
182: $hasTypes = false;
183: $builder = ConstantArrayTypeBuilder::createEmpty();
184: foreach ($optArrayType->getIterableKeyType()->getConstantScalarTypes() as $optType) {
185: $optValue = $optType->getValue();
186:
187: if (!is_int($optValue)) {
188: $hasTypes = false;
189: break;
190: }
191:
192: $optValueType = self::getCurlOptValueType($optValue);
193: if ($optValueType === null) {
194: $hasTypes = false;
195: break;
196: }
197:
198: $hasTypes = true;
199: $builder->setOffsetValueType(
200: new ConstantIntegerType($optValue),
201: $optValueType,
202: !$optArrayType->hasOffsetValueType($optType)->yes(),
203: );
204: }
205:
206: $acceptor = $parametersAcceptors[0];
207: $parameters = $acceptor->getParameters();
208: if ($hasTypes && isset($parameters[1])) {
209: $parameters[1] = new NativeParameterReflection(
210: $parameters[1]->getName(),
211: $parameters[1]->isOptional(),
212: $builder->getArray(),
213: $parameters[1]->passedByReference(),
214: $parameters[1]->isVariadic(),
215: $parameters[1]->getDefaultValue(),
216: );
217:
218: $parametersAcceptors = [
219: new FunctionVariant(
220: $acceptor->getTemplateTypeMap(),
221: $acceptor->getResolvedTemplateTypeMap(),
222: $parameters,
223: $acceptor->isVariadic(),
224: $acceptor->getReturnType(),
225: $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(),
226: ),
227: ];
228: }
229: }
230:
231: if ((bool) $args[0]->getAttribute(ArrayFilterArgVisitor::ATTRIBUTE_NAME)) {
232: $arrayFilterParameters = null;
233: $nativeArrayFilterParameters = null;
234: if (isset($args[2])) {
235: $mode = $scope->getType($args[2]->value);
236: if ($mode instanceof ConstantIntegerType) {
237: if ($mode->getValue() === ARRAY_FILTER_USE_KEY) {
238: $arrayFilterParameters = [
239: new DummyParameter('key', $scope->getIterableKeyType($scope->getType($args[0]->value)), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null),
240: ];
241: $nativeArrayFilterParameters = [
242: new DummyParameter('key', $scope->getIterableKeyType($scope->getNativeType($args[0]->value)), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null),
243: ];
244: } elseif ($mode->getValue() === ARRAY_FILTER_USE_BOTH) {
245: $arrayFilterParameters = [
246: new DummyParameter('item', $scope->getIterableValueType($scope->getType($args[0]->value)), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null),
247: new DummyParameter('key', $scope->getIterableKeyType($scope->getType($args[0]->value)), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null),
248: ];
249: $nativeArrayFilterParameters = [
250: new DummyParameter('item', $scope->getIterableValueType($scope->getNativeType($args[0]->value)), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null),
251: new DummyParameter('key', $scope->getIterableKeyType($scope->getNativeType($args[0]->value)), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null),
252: ];
253: }
254: }
255: }
256:
257: $acceptor = $parametersAcceptors[0];
258: $parameters = $acceptor->getParameters();
259: if (isset($parameters[1])) {
260: $arrayArgType = $scope->getType($args[0]->value);
261: $callableType = new UnionType([
262: new CallableType(
263: $arrayFilterParameters ?? [
264: new DummyParameter('item', $scope->getIterableValueType($arrayArgType), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null),
265: ],
266: new BooleanType(),
267: false,
268: ),
269: new NullType(),
270: ]);
271: $nativeArrayArgType = $scope->getNativeType($args[0]->value);
272: $nativeCallableType = new UnionType([
273: new CallableType(
274: $nativeArrayFilterParameters ?? [
275: new DummyParameter('item', $scope->getIterableValueType($nativeArrayArgType), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null),
276: ],
277: new BooleanType(),
278: false,
279: ),
280: new NullType(),
281: ]);
282: $parameters[1] = self::overrideParameterType($parameters[1], $callableType, $nativeCallableType);
283: $parametersAcceptors = [self::overrideAcceptorParameters($acceptor, $parameters)];
284: }
285: }
286:
287: if (count($args) <= 2 && (bool) $args[0]->getAttribute(ImplodeArgVisitor::ATTRIBUTE_NAME)) {
288: $acceptor = $namedArgumentsVariants[0] ?? $parametersAcceptors[0];
289: $parameters = $acceptor->getParameters();
290: if (
291: (isset($args[1]) || ($args[0]->name !== null && $args[0]->name->name === 'array'))
292: && isset($parameters[0]) && isset($parameters[1])
293: ) {
294: $parameters = [
295: new NativeParameterReflection($parameters[0]->getName(), optional: false, type: new StringType(), passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null),
296: new NativeParameterReflection($parameters[1]->getName(), optional: false, type: new ArrayType(new MixedType(), new MixedType()), passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null),
297: ];
298: } elseif (isset($parameters[0])) {
299: $parameters = [
300: new NativeParameterReflection($parameters[0]->getName(), optional: false, type: new ArrayType(new MixedType(), new MixedType()), passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null),
301: ];
302: }
303:
304: $parametersAcceptors = [
305: new FunctionVariant(
306: $acceptor->getTemplateTypeMap(),
307: $acceptor->getResolvedTemplateTypeMap(),
308: $parameters,
309: $acceptor->isVariadic(),
310: $acceptor->getReturnType(),
311: $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(),
312: ),
313: ];
314: }
315:
316: if ((bool) $args[0]->getAttribute(ArrayWalkArgVisitor::ATTRIBUTE_NAME)) {
317: $arrayArgType = $scope->getType($args[0]->value);
318: $nativeArrayArgType = $scope->getNativeType($args[0]->value);
319: $arrayWalkParameters = [
320: new DummyParameter('item', $scope->getIterableValueType($arrayArgType), optional: false, passedByReference: PassedByReference::createReadsArgument(), variadic: false, defaultValue: null),
321: new DummyParameter('key', $scope->getIterableKeyType($arrayArgType), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null),
322: ];
323: $nativeArrayWalkParameters = [
324: new DummyParameter('item', $scope->getIterableValueType($nativeArrayArgType), optional: false, passedByReference: PassedByReference::createReadsArgument(), variadic: false, defaultValue: null),
325: new DummyParameter('key', $scope->getIterableKeyType($nativeArrayArgType), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null),
326: ];
327: if (isset($args[2])) {
328: $arrayWalkParameters[] = new DummyParameter('arg', $scope->getType($args[2]->value), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null);
329: $nativeArrayWalkParameters[] = new DummyParameter('arg', $scope->getNativeType($args[2]->value), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null);
330: }
331:
332: $acceptor = $parametersAcceptors[0];
333: $parameters = $acceptor->getParameters();
334: if (isset($parameters[1])) {
335: $callableType = new CallableType($arrayWalkParameters, new MixedType(), false);
336: $nativeCallableType = new CallableType($nativeArrayWalkParameters, new MixedType(), false);
337: $parameters[1] = self::overrideParameterType($parameters[1], $callableType, $nativeCallableType);
338: $parametersAcceptors = [self::overrideAcceptorParameters($acceptor, $parameters)];
339: }
340: }
341:
342: if ((bool) $args[0]->getAttribute(ArrayFindArgVisitor::ATTRIBUTE_NAME)) {
343: $acceptor = $parametersAcceptors[0];
344: $parameters = $acceptor->getParameters();
345: if (isset($parameters[1])) {
346: $argType = $scope->getType($args[0]->value);
347: $callableType = new CallableType(
348: [
349: new DummyParameter('value', $scope->getIterableValueType($argType), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null),
350: new DummyParameter('key', $scope->getIterableKeyType($argType), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null),
351: ],
352: new BooleanType(),
353: false,
354: );
355: $nativeArgType = $scope->getNativeType($args[0]->value);
356: $nativeCallableType = new CallableType(
357: [
358: new DummyParameter('value', $scope->getIterableValueType($nativeArgType), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null),
359: new DummyParameter('key', $scope->getIterableKeyType($nativeArgType), optional: false, passedByReference: PassedByReference::createNo(), variadic: false, defaultValue: null),
360: ],
361: new BooleanType(),
362: false,
363: );
364: $parameters[1] = self::overrideParameterType($parameters[1], $callableType, $nativeCallableType);
365: $parametersAcceptors = [self::overrideAcceptorParameters($acceptor, $parameters)];
366: }
367: }
368:
369: $closureBindToVar = $args[0]->getAttribute(ClosureBindToVarVisitor::ATTRIBUTE_NAME);
370: if (
371: $closureBindToVar instanceof Node\Expr\Variable
372: && is_string($closureBindToVar->name)
373: ) {
374: $varType = $scope->getType($closureBindToVar);
375: if ((new ObjectType(Closure::class))->isSuperTypeOf($varType)->yes()) {
376: $inFunction = $scope->getFunction();
377: if ($inFunction !== null) {
378: $closureThisParameters = [];
379: foreach ($inFunction->getParameters() as $parameter) {
380: if ($parameter->getClosureThisType() === null) {
381: continue;
382: }
383: $closureThisParameters[$parameter->getName()] = $parameter->getClosureThisType();
384: }
385: if (array_key_exists($closureBindToVar->name, $closureThisParameters)) {
386: if ($scope->hasExpressionType(new ParameterVariableOriginalValueExpr($closureBindToVar->name))->yes()) {
387: $acceptor = $parametersAcceptors[0];
388: $parameters = $acceptor->getParameters();
389: if (isset($parameters[0])) {
390: $parameters[0] = new NativeParameterReflection(
391: $parameters[0]->getName(),
392: $parameters[0]->isOptional(),
393: $closureThisParameters[$closureBindToVar->name],
394: $parameters[0]->passedByReference(),
395: $parameters[0]->isVariadic(),
396: $parameters[0]->getDefaultValue(),
397: );
398: $parametersAcceptors = [
399: new FunctionVariant(
400: $acceptor->getTemplateTypeMap(),
401: $acceptor->getResolvedTemplateTypeMap(),
402: $parameters,
403: $acceptor->isVariadic(),
404: $acceptor->getReturnType(),
405: $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(),
406: ),
407: ];
408: }
409: }
410: }
411: }
412: }
413: }
414:
415: if (
416: $args[0]->getAttribute(ClosureBindArgVisitor::ATTRIBUTE_NAME) !== null
417: && $args[0]->value instanceof Node\Expr\Variable
418: && is_string($args[0]->value->name)
419: ) {
420: $closureVarName = $args[0]->value->name;
421: $inFunction = $scope->getFunction();
422: if ($inFunction !== null) {
423: $closureThisParameters = [];
424: foreach ($inFunction->getParameters() as $parameter) {
425: if ($parameter->getClosureThisType() === null) {
426: continue;
427: }
428: $closureThisParameters[$parameter->getName()] = $parameter->getClosureThisType();
429: }
430: if (array_key_exists($closureVarName, $closureThisParameters)) {
431: if ($scope->hasExpressionType(new ParameterVariableOriginalValueExpr($closureVarName))->yes()) {
432: $acceptor = $parametersAcceptors[0];
433: $parameters = $acceptor->getParameters();
434:
435: if (isset($parameters[1])) {
436: $parameters[1] = new NativeParameterReflection(
437: $parameters[1]->getName(),
438: $parameters[1]->isOptional(),
439: $closureThisParameters[$closureVarName],
440: $parameters[1]->passedByReference(),
441: $parameters[1]->isVariadic(),
442: $parameters[1]->getDefaultValue(),
443: );
444: $parametersAcceptors = [
445: new FunctionVariant(
446: $acceptor->getTemplateTypeMap(),
447: $acceptor->getResolvedTemplateTypeMap(),
448: $parameters,
449: $acceptor->isVariadic(),
450: $acceptor->getReturnType(),
451: $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(),
452: ),
453: ];
454: }
455: }
456: }
457: }
458: }
459: }
460:
461: if (count($parametersAcceptors) === 1) {
462: $acceptor = $parametersAcceptors[0];
463: if (!self::hasAcceptorTemplateOrLateResolvableType($acceptor)) {
464: return $acceptor;
465: }
466: }
467:
468: $reorderedArgs = $args;
469: $parameters = null;
470: $singleParametersAcceptor = null;
471: if (count($parametersAcceptors) === 1) {
472: if (!array_is_list($args)) {
473: // actually $args parameter should be typed to list but we can't atm,
474: // because its a BC break.
475: $args = array_values($args);
476: }
477: $reorderedArgs = ArgumentsNormalizer::reorderArgs($parametersAcceptors[0], $args);
478: $singleParametersAcceptor = $parametersAcceptors[0];
479: }
480:
481: $hasName = false;
482: foreach ($reorderedArgs ?? $args as $i => $arg) {
483: $originalArg = $arg->getAttribute(ArgumentsNormalizer::ORIGINAL_ARG_ATTRIBUTE) ?? $arg;
484: $parameter = null;
485: if ($singleParametersAcceptor !== null) {
486: $parameters = $singleParametersAcceptor->getParameters();
487: if (isset($parameters[$i])) {
488: $parameter = $parameters[$i];
489: } elseif (count($parameters) > 0 && $singleParametersAcceptor->isVariadic()) {
490: $parameter = array_last($parameters);
491: }
492: }
493:
494: if ($parameter !== null && $scope instanceof MutatingScope) {
495: $rememberTypes = !$originalArg->value instanceof Node\Expr\Closure && !$originalArg->value instanceof Node\Expr\ArrowFunction;
496: $scope = $scope->pushInFunctionCall(null, $parameter, $rememberTypes);
497: }
498:
499: $type = $scope->getType($originalArg->value);
500:
501: if ($parameter !== null && $scope instanceof MutatingScope) {
502: $scope = $scope->popInFunctionCall();
503: }
504:
505: if ($originalArg->name !== null) {
506: $index = $originalArg->name->toString();
507: $hasName = true;
508: } else {
509: $index = $i;
510: }
511: if ($originalArg->unpack) {
512: $unpack = true;
513: $constantArrays = $type->getConstantArrays();
514: if (count($constantArrays) > 0) {
515: foreach ($constantArrays as $constantArray) {
516: $values = $constantArray->getValueTypes();
517: foreach ($constantArray->getKeyTypes() as $j => $keyType) {
518: $valueType = $values[$j];
519: $valueIndex = $keyType->getValue();
520: if (is_string($valueIndex)) {
521: $hasName = true;
522: } else {
523: $valueIndex = $i + $j;
524: }
525:
526: $types[$valueIndex] = isset($types[$valueIndex])
527: ? TypeCombinator::union($types[$valueIndex], $valueType)
528: : $valueType;
529: }
530: }
531: } else {
532: $types[$index] = $type->getIterableValueType();
533: }
534: } else {
535: $types[$index] = $type;
536: }
537: }
538:
539: if ($hasName && $namedArgumentsVariants !== null) {
540: return self::selectFromTypes($types, $namedArgumentsVariants, $unpack);
541: }
542:
543: return self::selectFromTypes($types, $parametersAcceptors, $unpack);
544: }
545:
546: private static function hasAcceptorTemplateOrLateResolvableType(ParametersAcceptor $acceptor): bool
547: {
548: if ($acceptor->getReturnType()->hasTemplateOrLateResolvableType()) {
549: return true;
550: }
551:
552: foreach ($acceptor->getParameters() as $parameter) {
553: if (
554: $parameter instanceof ExtendedParameterReflection
555: && $parameter->getOutType() !== null
556: && $parameter->getOutType()->hasTemplateOrLateResolvableType()
557: ) {
558: return true;
559: }
560:
561: if (
562: $parameter instanceof ExtendedParameterReflection
563: && $parameter->getClosureThisType() !== null
564: && $parameter->getClosureThisType()->hasTemplateOrLateResolvableType()
565: ) {
566: return true;
567: }
568:
569: if (!$parameter->getType()->hasTemplateOrLateResolvableType()) {
570: continue;
571: }
572:
573: return true;
574: }
575:
576: return false;
577: }
578:
579: /**
580: * @param array<int|string, Type> $types
581: * @param ParametersAcceptor[] $parametersAcceptors
582: */
583: public static function selectFromTypes(
584: array $types,
585: array $parametersAcceptors,
586: bool $unpack,
587: ): ParametersAcceptor
588: {
589: if (count($parametersAcceptors) === 1) {
590: return GenericParametersAcceptorResolver::resolve($types, $parametersAcceptors[0]);
591: }
592:
593: if (count($parametersAcceptors) === 0) {
594: throw new ShouldNotHappenException(
595: 'getVariants() must return at least one variant.',
596: );
597: }
598:
599: $typesCount = count($types);
600: $acceptableAcceptors = [];
601:
602: foreach ($parametersAcceptors as $parametersAcceptor) {
603: if ($unpack) {
604: $acceptableAcceptors[] = $parametersAcceptor;
605: continue;
606: }
607:
608: $functionParametersMinCount = 0;
609: $functionParametersMaxCount = 0;
610: foreach ($parametersAcceptor->getParameters() as $parameter) {
611: if (!$parameter->isOptional()) {
612: $functionParametersMinCount++;
613: }
614:
615: $functionParametersMaxCount++;
616: }
617:
618: if ($typesCount < $functionParametersMinCount) {
619: continue;
620: }
621:
622: if (
623: !$parametersAcceptor->isVariadic()
624: && $typesCount > $functionParametersMaxCount
625: ) {
626: continue;
627: }
628:
629: $acceptableAcceptors[] = $parametersAcceptor;
630: }
631:
632: if (count($acceptableAcceptors) === 0) {
633: return GenericParametersAcceptorResolver::resolve($types, self::combineAcceptors($parametersAcceptors));
634: }
635:
636: if (count($acceptableAcceptors) === 1) {
637: return GenericParametersAcceptorResolver::resolve($types, $acceptableAcceptors[0]);
638: }
639:
640: $winningAcceptors = [];
641: $winningCertainty = null;
642: foreach ($acceptableAcceptors as $acceptableAcceptor) {
643: $isSuperType = TrinaryLogic::createYes();
644: $acceptableAcceptor = GenericParametersAcceptorResolver::resolve($types, $acceptableAcceptor);
645: foreach ($acceptableAcceptor->getParameters() as $i => $parameter) {
646: if (!isset($types[$i])) {
647: if (!$unpack || count($types) <= 0) {
648: break;
649: }
650:
651: $type = $types[array_key_last($types)];
652: } else {
653: $type = $types[$i];
654: }
655:
656: if ($parameter->getType() instanceof MixedType) {
657: $isSuperType = $isSuperType->and(TrinaryLogic::createMaybe());
658: } else {
659: $isSuperType = $isSuperType->and($parameter->getType()->isSuperTypeOf($type)->result);
660: }
661: }
662:
663: if ($isSuperType->no()) {
664: continue;
665: }
666:
667: if ($winningCertainty === null) {
668: $winningAcceptors[] = $acceptableAcceptor;
669: $winningCertainty = $isSuperType;
670: } else {
671: $comparison = $winningCertainty->compareTo($isSuperType);
672: if ($comparison === $isSuperType) {
673: $winningAcceptors = [$acceptableAcceptor];
674: $winningCertainty = $isSuperType;
675: } elseif ($comparison === null) {
676: $winningAcceptors[] = $acceptableAcceptor;
677: }
678: }
679: }
680:
681: if (count($winningAcceptors) === 0) {
682: return GenericParametersAcceptorResolver::resolve($types, self::combineAcceptors($acceptableAcceptors));
683: }
684:
685: return GenericParametersAcceptorResolver::resolve($types, self::combineAcceptors($winningAcceptors));
686: }
687:
688: /**
689: * @param ParametersAcceptor[] $acceptors
690: */
691: public static function combineAcceptors(array $acceptors): ExtendedParametersAcceptor
692: {
693: if (count($acceptors) === 0) {
694: throw new ShouldNotHappenException(
695: 'getVariants() must return at least one variant.',
696: );
697: }
698: if (count($acceptors) === 1) {
699: return self::wrapAcceptor($acceptors[0]);
700: }
701:
702: $minimumNumberOfParameters = null;
703: foreach ($acceptors as $acceptor) {
704: $acceptorParametersMinCount = 0;
705: foreach ($acceptor->getParameters() as $parameter) {
706: if ($parameter->isOptional()) {
707: continue;
708: }
709:
710: $acceptorParametersMinCount++;
711: }
712:
713: if ($minimumNumberOfParameters !== null && $minimumNumberOfParameters <= $acceptorParametersMinCount) {
714: continue;
715: }
716:
717: $minimumNumberOfParameters = $acceptorParametersMinCount;
718: }
719:
720: $parameters = [];
721: $isVariadic = false;
722: $returnTypes = [];
723: $phpDocReturnTypes = [];
724: $nativeReturnTypes = [];
725: $callableOccurred = false;
726: $throwPoints = [];
727: $isPure = TrinaryLogic::createNo();
728: $impurePoints = [];
729: $invalidateExpressions = [];
730: $usedVariables = [];
731: $acceptsNamedArguments = TrinaryLogic::createNo();
732: $mustUseReturnValue = TrinaryLogic::createMaybe();
733: $isStaticClosure = TrinaryLogic::createMaybe();
734:
735: foreach ($acceptors as $acceptor) {
736: $returnTypes[] = $acceptor->getReturnType();
737:
738: if ($acceptor instanceof ExtendedParametersAcceptor) {
739: $phpDocReturnTypes[] = $acceptor->getPhpDocReturnType();
740: $nativeReturnTypes[] = $acceptor->getNativeReturnType();
741: }
742: if ($acceptor instanceof CallableParametersAcceptor) {
743: $callableOccurred = true;
744: $throwPoints = array_merge($throwPoints, $acceptor->getThrowPoints());
745: $isPure = $isPure->or($acceptor->isPure());
746: $impurePoints = array_merge($impurePoints, $acceptor->getImpurePoints());
747: $invalidateExpressions = array_merge($invalidateExpressions, $acceptor->getInvalidateExpressions());
748: $usedVariables = array_merge($usedVariables, $acceptor->getUsedVariables());
749: $acceptsNamedArguments = $acceptsNamedArguments->or($acceptor->acceptsNamedArguments());
750: $mustUseReturnValue = $mustUseReturnValue->or($acceptor->mustUseReturnValue());
751: $isStaticClosure = $isStaticClosure->or($acceptor->isStaticClosure());
752: }
753: $isVariadic = $isVariadic || $acceptor->isVariadic();
754:
755: foreach ($acceptor->getParameters() as $i => $parameter) {
756: if (!isset($parameters[$i])) {
757: $parameters[$i] = new ExtendedDummyParameter(
758: $parameter->getName(),
759: $parameter->getType(),
760: $i + 1 > $minimumNumberOfParameters,
761: $parameter->passedByReference(),
762: $parameter->isVariadic(),
763: $parameter->getDefaultValue(),
764: $parameter instanceof ExtendedParameterReflection ? $parameter->getNativeType() : new MixedType(),
765: $parameter instanceof ExtendedParameterReflection ? $parameter->getPhpDocType() : new MixedType(),
766: $parameter instanceof ExtendedParameterReflection ? $parameter->getOutType() : null,
767: $parameter instanceof ExtendedParameterReflection ? $parameter->isImmediatelyInvokedCallable() : TrinaryLogic::createMaybe(),
768: $parameter instanceof ExtendedParameterReflection ? $parameter->getClosureThisType() : null,
769: $parameter instanceof ExtendedParameterReflection ? $parameter->getAttributes() : [],
770: $parameter instanceof ExtendedParameterReflection ? $parameter->getAllowedConstants() : null,
771: $parameter instanceof ExtendedParameterReflection ? $parameter->isPureUnlessCallableIsImpureParameter() : TrinaryLogic::createNo(),
772: );
773: continue;
774: }
775:
776: $isVariadic = $parameters[$i]->isVariadic() || $parameter->isVariadic();
777: $defaultValueLeft = $parameters[$i]->getDefaultValue();
778: $defaultValueRight = $parameter->getDefaultValue();
779: if ($defaultValueLeft !== null && $defaultValueRight !== null) {
780: $defaultValue = TypeCombinator::union($defaultValueLeft, $defaultValueRight);
781: } else {
782: $defaultValue = null;
783: }
784:
785: $type = TypeCombinator::union($parameters[$i]->getType(), $parameter->getType());
786: $nativeType = $parameters[$i]->getNativeType();
787: $phpDocType = $parameters[$i]->getPhpDocType();
788: $outType = $parameters[$i]->getOutType();
789: $immediatelyInvokedCallable = $parameters[$i]->isImmediatelyInvokedCallable();
790: $closureThisType = $parameters[$i]->getClosureThisType();
791: $attributes = $parameters[$i]->getAttributes();
792: if ($parameter instanceof ExtendedParameterReflection) {
793: $nativeType = TypeCombinator::union($nativeType, $parameter->getNativeType());
794: $phpDocType = TypeCombinator::union($phpDocType, $parameter->getPhpDocType());
795:
796: if ($parameter->getOutType() !== null) {
797: $outType = $outType === null ? null : TypeCombinator::union($outType, $parameter->getOutType());
798: } else {
799: $outType = null;
800: }
801:
802: if ($parameter->getClosureThisType() !== null && $closureThisType !== null) {
803: $closureThisType = TypeCombinator::union($closureThisType, $parameter->getClosureThisType());
804: } else {
805: $closureThisType = null;
806: }
807:
808: $immediatelyInvokedCallable = $parameter->isImmediatelyInvokedCallable()->or($immediatelyInvokedCallable);
809: $attributes = array_merge($attributes, $parameter->getAttributes());
810: } else {
811: $nativeType = new MixedType();
812: $phpDocType = $type;
813: $outType = null;
814: $immediatelyInvokedCallable = TrinaryLogic::createMaybe();
815: $closureThisType = null;
816: }
817:
818: $allowedConstants = $parameters[$i]->getAllowedConstants();
819: if ($allowedConstants !== null) {
820: $otherAllowedConstants = $parameter instanceof ExtendedParameterReflection ? $parameter->getAllowedConstants() : null;
821: if ($otherAllowedConstants === null || !$allowedConstants->equals($otherAllowedConstants)) {
822: $allowedConstants = null;
823: }
824: }
825:
826: $leftPureUnless = $parameters[$i]->isPureUnlessCallableIsImpureParameter();
827: $rightPureUnless = $parameter instanceof ExtendedParameterReflection ? $parameter->isPureUnlessCallableIsImpureParameter() : TrinaryLogic::createNo();
828: $pureUnlessCallableIsImpureParameter = $leftPureUnless->equals($rightPureUnless) ? $leftPureUnless : TrinaryLogic::createMaybe();
829:
830: $parameters[$i] = new ExtendedDummyParameter(
831: $parameters[$i]->getName() !== $parameter->getName() ? sprintf('%s|%s', $parameters[$i]->getName(), $parameter->getName()) : $parameter->getName(),
832: $type,
833: $i + 1 > $minimumNumberOfParameters,
834: $parameters[$i]->passedByReference()->combine($parameter->passedByReference()),
835: $isVariadic,
836: $defaultValue,
837: $nativeType,
838: $phpDocType,
839: $outType,
840: $immediatelyInvokedCallable,
841: $closureThisType,
842: $attributes,
843: $allowedConstants,
844: $pureUnlessCallableIsImpureParameter,
845: );
846:
847: if ($isVariadic) {
848: $parameters = array_slice($parameters, 0, $i + 1);
849: break;
850: }
851: }
852: }
853:
854: $returnType = TypeCombinator::union(...$returnTypes);
855: $phpDocReturnType = $phpDocReturnTypes === [] ? null : TypeCombinator::union(...$phpDocReturnTypes);
856: $nativeReturnType = $nativeReturnTypes === [] ? null : TypeCombinator::union(...$nativeReturnTypes);
857:
858: if ($callableOccurred) {
859: return new ExtendedCallableFunctionVariant(
860: TemplateTypeMap::createEmpty(),
861: null,
862: array_values($parameters),
863: $isVariadic,
864: $returnType,
865: $phpDocReturnType ?? $returnType,
866: $nativeReturnType ?? new MixedType(),
867: null,
868: $throwPoints,
869: $isPure,
870: $impurePoints,
871: $invalidateExpressions,
872: $usedVariables,
873: $acceptsNamedArguments,
874: $mustUseReturnValue,
875: isStatic: $isStaticClosure,
876: );
877: }
878:
879: return new ExtendedFunctionVariant(
880: TemplateTypeMap::createEmpty(),
881: null,
882: array_values($parameters),
883: $isVariadic,
884: $returnType,
885: $phpDocReturnType ?? $returnType,
886: $nativeReturnType ?? new MixedType(),
887: );
888: }
889:
890: private static function wrapAcceptor(ParametersAcceptor $acceptor): ExtendedParametersAcceptor
891: {
892: if ($acceptor instanceof ExtendedParametersAcceptor) {
893: return $acceptor;
894: }
895:
896: if ($acceptor instanceof CallableParametersAcceptor) {
897: return new ExtendedCallableFunctionVariant(
898: $acceptor->getTemplateTypeMap(),
899: $acceptor->getResolvedTemplateTypeMap(),
900: array_map(static fn (ParameterReflection $parameter): ExtendedParameterReflection => self::wrapParameter($parameter), $acceptor->getParameters()),
901: $acceptor->isVariadic(),
902: $acceptor->getReturnType(),
903: $acceptor->getReturnType(),
904: new MixedType(),
905: TemplateTypeVarianceMap::createEmpty(),
906: $acceptor->getThrowPoints(),
907: $acceptor->isPure(),
908: $acceptor->getImpurePoints(),
909: $acceptor->getInvalidateExpressions(),
910: $acceptor->getUsedVariables(),
911: $acceptor->acceptsNamedArguments(),
912: $acceptor->mustUseReturnValue(),
913: $acceptor->getAsserts(),
914: $acceptor->isStaticClosure(),
915: );
916: }
917:
918: return new ExtendedFunctionVariant(
919: $acceptor->getTemplateTypeMap(),
920: $acceptor->getResolvedTemplateTypeMap(),
921: array_map(static fn (ParameterReflection $parameter): ExtendedParameterReflection => self::wrapParameter($parameter), $acceptor->getParameters()),
922: $acceptor->isVariadic(),
923: $acceptor->getReturnType(),
924: $acceptor->getReturnType(),
925: new MixedType(),
926: TemplateTypeVarianceMap::createEmpty(),
927: );
928: }
929:
930: private static function wrapParameter(ParameterReflection $parameter): ExtendedParameterReflection
931: {
932: return $parameter instanceof ExtendedParameterReflection ? $parameter : new ExtendedDummyParameter(
933: $parameter->getName(),
934: $parameter->getType(),
935: $parameter->isOptional(),
936: $parameter->passedByReference(),
937: $parameter->isVariadic(),
938: $parameter->getDefaultValue(),
939: new MixedType(),
940: $parameter->getType(),
941: null,
942: TrinaryLogic::createMaybe(),
943: null,
944: [],
945: null,
946: TrinaryLogic::createNo(),
947: );
948: }
949:
950: private static function getCurlOptValueType(int $curlOpt): ?Type
951: {
952: if (defined('CURLOPT_SSL_VERIFYHOST') && $curlOpt === CURLOPT_SSL_VERIFYHOST) {
953: return new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(2)]);
954: }
955:
956: $boolConstants = [
957: 'CURLOPT_AUTOREFERER',
958: 'CURLOPT_COOKIESESSION',
959: 'CURLOPT_CERTINFO',
960: 'CURLOPT_CONNECT_ONLY',
961: 'CURLOPT_CRLF',
962: 'CURLOPT_DISALLOW_USERNAME_IN_URL',
963: 'CURLOPT_DNS_SHUFFLE_ADDRESSES',
964: 'CURLOPT_HAPROXYPROTOCOL',
965: 'CURLOPT_SSH_COMPRESSION',
966: 'CURLOPT_DNS_USE_GLOBAL_CACHE',
967: 'CURLOPT_FAILONERROR',
968: 'CURLOPT_SSL_FALSESTART',
969: 'CURLOPT_FILETIME',
970: 'CURLOPT_FOLLOWLOCATION',
971: 'CURLOPT_FORBID_REUSE',
972: 'CURLOPT_FRESH_CONNECT',
973: 'CURLOPT_FTP_USE_EPRT',
974: 'CURLOPT_FTP_USE_EPSV',
975: 'CURLOPT_FTP_CREATE_MISSING_DIRS',
976: 'CURLOPT_FTPAPPEND',
977: 'CURLOPT_TCP_NODELAY',
978: 'CURLOPT_FTPASCII',
979: 'CURLOPT_FTPLISTONLY',
980: 'CURLOPT_HEADER',
981: 'CURLOPT_HTTP09_ALLOWED',
982: 'CURLOPT_HTTPGET',
983: 'CURLOPT_HTTPPROXYTUNNEL',
984: 'CURLOPT_HTTP_CONTENT_DECODING',
985: 'CURLOPT_KEEP_SENDING_ON_ERROR',
986: 'CURLOPT_MUTE',
987: 'CURLOPT_NETRC',
988: 'CURLOPT_NOBODY',
989: 'CURLOPT_NOPROGRESS',
990: 'CURLOPT_NOSIGNAL',
991: 'CURLOPT_PATH_AS_IS',
992: 'CURLOPT_PIPEWAIT',
993: 'CURLOPT_POST',
994: 'CURLOPT_PUT',
995: 'CURLOPT_RETURNTRANSFER',
996: 'CURLOPT_SASL_IR',
997: 'CURLOPT_SSL_ENABLE_ALPN',
998: 'CURLOPT_SSL_ENABLE_NPN',
999: 'CURLOPT_SSL_VERIFYPEER',
1000: 'CURLOPT_SSL_VERIFYSTATUS',
1001: 'CURLOPT_PROXY_SSL_VERIFYPEER',
1002: 'CURLOPT_SUPPRESS_CONNECT_HEADERS',
1003: 'CURLOPT_TCP_FASTOPEN',
1004: 'CURLOPT_TFTP_NO_OPTIONS',
1005: 'CURLOPT_TRANSFERTEXT',
1006: 'CURLOPT_UNRESTRICTED_AUTH',
1007: 'CURLOPT_UPLOAD',
1008: 'CURLOPT_VERBOSE',
1009: ];
1010: foreach ($boolConstants as $constName) {
1011: if (defined($constName) && constant($constName) === $curlOpt) {
1012: return new BooleanType();
1013: }
1014: }
1015:
1016: $intConstants = [
1017: 'CURLOPT_BUFFERSIZE',
1018: 'CURLOPT_CONNECTTIMEOUT',
1019: 'CURLOPT_CONNECTTIMEOUT_MS',
1020: 'CURLOPT_DNS_CACHE_TIMEOUT',
1021: 'CURLOPT_EXPECT_100_TIMEOUT_MS',
1022: 'CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS',
1023: 'CURLOPT_FTPSSLAUTH',
1024: 'CURLOPT_HEADEROPT',
1025: 'CURLOPT_HTTP_VERSION',
1026: 'CURLOPT_HTTPAUTH',
1027: 'CURLOPT_INFILESIZE',
1028: 'CURLOPT_LOW_SPEED_LIMIT',
1029: 'CURLOPT_LOW_SPEED_TIME',
1030: 'CURLOPT_MAXCONNECTS',
1031: 'CURLOPT_MAXREDIRS',
1032: 'CURLOPT_PORT',
1033: 'CURLOPT_POSTREDIR',
1034: 'CURLOPT_PROTOCOLS',
1035: 'CURLOPT_PROXYAUTH',
1036: 'CURLOPT_PROXYPORT',
1037: 'CURLOPT_PROXYTYPE',
1038: 'CURLOPT_REDIR_PROTOCOLS',
1039: 'CURLOPT_RESUME_FROM',
1040: 'CURLOPT_SOCKS5_AUTH',
1041: 'CURLOPT_SSL_OPTIONS',
1042: 'CURLOPT_SSL_VERIFYHOST',
1043: 'CURLOPT_SSLVERSION',
1044: 'CURLOPT_PROXY_SSL_OPTIONS',
1045: 'CURLOPT_PROXY_SSL_VERIFYHOST',
1046: 'CURLOPT_PROXY_SSLVERSION',
1047: 'CURLOPT_STREAM_WEIGHT',
1048: 'CURLOPT_TCP_KEEPALIVE',
1049: 'CURLOPT_TCP_KEEPIDLE',
1050: 'CURLOPT_TCP_KEEPINTVL',
1051: 'CURLOPT_TIMECONDITION',
1052: 'CURLOPT_TIMEOUT',
1053: 'CURLOPT_TIMEOUT_MS',
1054: 'CURLOPT_TIMEVALUE',
1055: 'CURLOPT_TIMEVALUE_LARGE',
1056: 'CURLOPT_MAX_RECV_SPEED_LARGE',
1057: 'CURLOPT_SSH_AUTH_TYPES',
1058: 'CURLOPT_IPRESOLVE',
1059: 'CURLOPT_FTP_FILEMETHOD',
1060: ];
1061: foreach ($intConstants as $constName) {
1062: if (defined($constName) && constant($constName) === $curlOpt) {
1063: return new IntegerType();
1064: }
1065: }
1066:
1067: $nullableStringConstants = [
1068: 'CURLOPT_CUSTOMREQUEST',
1069: 'CURLOPT_DNS_INTERFACE',
1070: 'CURLOPT_DNS_LOCAL_IP4',
1071: 'CURLOPT_DNS_LOCAL_IP6',
1072: 'CURLOPT_DOH_URL',
1073: 'CURLOPT_FTP_ACCOUNT',
1074: 'CURLOPT_FTPPORT',
1075: 'CURLOPT_HSTS',
1076: 'CURLOPT_KRBLEVEL',
1077: 'CURLOPT_RANGE',
1078: 'CURLOPT_RTSP_SESSION_ID',
1079: 'CURLOPT_UNIX_SOCKET_PATH',
1080: 'CURLOPT_XOAUTH2_BEARER',
1081: ];
1082: foreach ($nullableStringConstants as $constName) {
1083: if (defined($constName) && constant($constName) === $curlOpt) {
1084: return new UnionType([
1085: new NullType(),
1086: new IntersectionType([
1087: new StringType(),
1088: new AccessoryNonEmptyStringType(),
1089: ]),
1090: ]);
1091: }
1092: }
1093:
1094: $nonEmptyStringConstants = [
1095: 'CURLOPT_ABSTRACT_UNIX_SOCKET',
1096: 'CURLOPT_ALTSVC',
1097: 'CURLOPT_AWS_SIGV4',
1098: 'CURLOPT_CAINFO',
1099: 'CURLOPT_CAPATH',
1100: 'CURLOPT_COOKIE',
1101: 'CURLOPT_COOKIEJAR',
1102: 'CURLOPT_COOKIELIST',
1103: 'CURLOPT_DEFAULT_PROTOCOL',
1104: 'CURLOPT_DNS_SERVERS',
1105: 'CURLOPT_EGDSOCKET',
1106: 'CURLOPT_FTP_ALTERNATIVE_TO_USER',
1107: 'CURLOPT_INTERFACE',
1108: 'CURLOPT_KEYPASSWD',
1109: 'CURLOPT_KRB4LEVEL',
1110: 'CURLOPT_LOGIN_OPTIONS',
1111: 'CURLOPT_MAIL_AUTH',
1112: 'CURLOPT_MAIL_FROM',
1113: 'CURLOPT_NOPROXY',
1114: 'CURLOPT_PASSWORD',
1115: 'CURLOPT_PINNEDPUBLICKEY',
1116: 'CURLOPT_PROTOCOLS_STR',
1117: 'CURLOPT_PROXY_CAINFO',
1118: 'CURLOPT_PROXY_CAPATH',
1119: 'CURLOPT_PROXY_CRLFILE',
1120: 'CURLOPT_PROXY_ISSUERCERT',
1121: 'CURLOPT_PROXY_KEYPASSWD',
1122: 'CURLOPT_PROXY_PINNEDPUBLICKEY',
1123: 'CURLOPT_PROXY_SERVICE_NAME',
1124: 'CURLOPT_PROXY_SSL_CIPHER_LIST',
1125: 'CURLOPT_PROXY_SSLCERT',
1126: 'CURLOPT_PROXY_SSLCERTTYPE',
1127: 'CURLOPT_PROXY_SSLKEY',
1128: 'CURLOPT_PROXY_SSLKEYTYPE',
1129: 'CURLOPT_PROXY_TLS13_CIPHERS',
1130: 'CURLOPT_PROXY_TLSAUTH_PASSWORD',
1131: 'CURLOPT_PROXY_TLSAUTH_TYPE',
1132: 'CURLOPT_PROXY_TLSAUTH_USERNAME',
1133: 'CURLOPT_PROXYPASSWORD',
1134: 'CURLOPT_PROXYUSERNAME',
1135: 'CURLOPT_PROXYUSERPWD',
1136: 'CURLOPT_RANDOM_FILE',
1137: 'CURLOPT_REDIR_PROTOCOLS_STR',
1138: 'CURLOPT_REFERER',
1139: 'CURLOPT_REQUEST_TARGET',
1140: 'CURLOPT_RTSP_STREAM_URI',
1141: 'CURLOPT_RTSP_TRANSPORT',
1142: 'CURLOPT_SASL_AUTHZID',
1143: 'CURLOPT_SERVICE_NAME',
1144: 'CURLOPT_SOCKS5_GSSAPI_SERVICE',
1145: 'CURLOPT_SSH_HOST_PUBLIC_KEY_MD5',
1146: 'CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256',
1147: 'CURLOPT_SSH_PRIVATE_KEYFILE',
1148: 'CURLOPT_SSH_PUBLIC_KEYFILE',
1149: 'CURLOPT_SSL_CIPHER_LIST',
1150: 'CURLOPT_SSL_EC_CURVES',
1151: 'CURLOPT_SSLCERT',
1152: 'CURLOPT_SSLCERTPASSWD',
1153: 'CURLOPT_SSLCERTTYPE',
1154: 'CURLOPT_SSLENGINE',
1155: 'CURLOPT_SSLENGINE_DEFAULT',
1156: 'CURLOPT_SSLKEY',
1157: 'CURLOPT_SSLKEYPASSWD',
1158: 'CURLOPT_SSLKEYTYPE',
1159: 'CURLOPT_TLS13_CIPHERS',
1160: 'CURLOPT_TLSAUTH_PASSWORD',
1161: 'CURLOPT_TLSAUTH_TYPE',
1162: 'CURLOPT_TLSAUTH_USERNAME',
1163: 'CURLOPT_TRANSFER_ENCODING',
1164: 'CURLOPT_URL',
1165: 'CURLOPT_USERAGENT',
1166: 'CURLOPT_USERNAME',
1167: 'CURLOPT_USERPWD',
1168: ];
1169: foreach ($nonEmptyStringConstants as $constName) {
1170: if (defined($constName) && constant($constName) === $curlOpt) {
1171: return new IntersectionType([
1172: new StringType(),
1173: new AccessoryNonEmptyStringType(),
1174: ]);
1175: }
1176: }
1177:
1178: $stringConstants = [
1179: 'CURLOPT_COOKIEFILE',
1180: 'CURLOPT_ENCODING', // Alias: CURLOPT_ACCEPT_ENCODING
1181: 'CURLOPT_PRE_PROXY',
1182: 'CURLOPT_PRIVATE',
1183: 'CURLOPT_PROXY',
1184: ];
1185: foreach ($stringConstants as $constName) {
1186: if (defined($constName) && constant($constName) === $curlOpt) {
1187: return new StringType();
1188: }
1189: }
1190:
1191: $intArrayStringKeysConstants = [
1192: 'CURLOPT_HTTPHEADER',
1193: ];
1194: foreach ($intArrayStringKeysConstants as $constName) {
1195: if (defined($constName) && constant($constName) === $curlOpt) {
1196: return new ArrayType(new IntegerType(), new StringType());
1197: }
1198: }
1199:
1200: $arrayConstants = [
1201: 'CURLOPT_CONNECT_TO',
1202: 'CURLOPT_HTTP200ALIASES',
1203: 'CURLOPT_POSTQUOTE',
1204: 'CURLOPT_PROXYHEADER',
1205: 'CURLOPT_QUOTE',
1206: 'CURLOPT_RESOLVE',
1207: ];
1208: foreach ($arrayConstants as $constName) {
1209: if (defined($constName) && constant($constName) === $curlOpt) {
1210: return new ArrayType(new MixedType(), new MixedType());
1211: }
1212: }
1213:
1214: $arrayOrStringConstants = [
1215: 'CURLOPT_POSTFIELDS',
1216: ];
1217: foreach ($arrayOrStringConstants as $constName) {
1218: if (defined($constName) && constant($constName) === $curlOpt) {
1219: return new UnionType([
1220: new StringType(),
1221: new ArrayType(new MixedType(), new MixedType()),
1222: ]);
1223: }
1224: }
1225:
1226: $resourceConstants = [
1227: 'CURLOPT_FILE',
1228: 'CURLOPT_INFILE',
1229: 'CURLOPT_STDERR',
1230: 'CURLOPT_WRITEHEADER',
1231: ];
1232: foreach ($resourceConstants as $constName) {
1233: if (defined($constName) && constant($constName) === $curlOpt) {
1234: return new ResourceType();
1235: }
1236: }
1237:
1238: if (defined('CURLOPT_SHARE') && $curlOpt === CURLOPT_SHARE) {
1239: $phpversion = PhpVersionStaticAccessor::getInstance();
1240:
1241: if ($phpversion->supportsCurlShareHandle()) {
1242: $shareType = new ObjectType('CurlShareHandle');
1243: } else {
1244: $shareType = new ResourceType();
1245: }
1246: if ($phpversion->supportsCurlSharePersistentHandle()) {
1247: $shareType = TypeCombinator::union($shareType, new ObjectType('CurlSharePersistentHandle'));
1248: }
1249:
1250: return $shareType;
1251: }
1252:
1253: // unknown constant
1254: return null;
1255: }
1256:
1257: private static function overrideParameterType(ParameterReflection $original, Type $type, Type $nativeType): ExtendedDummyParameter
1258: {
1259: $wrapped = self::wrapParameter($original);
1260:
1261: return new ExtendedDummyParameter(
1262: $wrapped->getName(),
1263: $type,
1264: $wrapped->isOptional(),
1265: $wrapped->passedByReference(),
1266: $wrapped->isVariadic(),
1267: $wrapped->getDefaultValue(),
1268: $nativeType,
1269: $type,
1270: $wrapped->getOutType(),
1271: $wrapped->isImmediatelyInvokedCallable(),
1272: $wrapped->getClosureThisType(),
1273: $wrapped->getAttributes(),
1274: $wrapped->getAllowedConstants(),
1275: $wrapped->isPureUnlessCallableIsImpureParameter(),
1276: );
1277: }
1278:
1279: /**
1280: * @param list<ParameterReflection> $parameters
1281: */
1282: private static function overrideAcceptorParameters(ParametersAcceptor $acceptor, array $parameters): ParametersAcceptor
1283: {
1284: if ($acceptor instanceof ExtendedParametersAcceptor) {
1285: return new ExtendedFunctionVariant(
1286: $acceptor->getTemplateTypeMap(),
1287: $acceptor->getResolvedTemplateTypeMap(),
1288: array_map(static fn (ParameterReflection $p): ExtendedParameterReflection => $p instanceof ExtendedParameterReflection ? $p : self::wrapParameter($p), $parameters),
1289: $acceptor->isVariadic(),
1290: $acceptor->getReturnType(),
1291: $acceptor->getPhpDocReturnType(),
1292: $acceptor->getNativeReturnType(),
1293: $acceptor->getCallSiteVarianceMap(),
1294: );
1295: }
1296:
1297: return new FunctionVariant(
1298: $acceptor->getTemplateTypeMap(),
1299: $acceptor->getResolvedTemplateTypeMap(),
1300: $parameters,
1301: $acceptor->isVariadic(),
1302: $acceptor->getReturnType(),
1303: TemplateTypeVarianceMap::createEmpty(),
1304: );
1305: }
1306:
1307: }
1308: