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