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