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_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) {
502: $scope = $scope->pushInFunctionCall(null, $parameter);
503: }
504:
505: $type = $scope->getType($originalArg->value);
506:
507: if ($parameter !== null && $scope instanceof MutatingScope) {
508: $scope = $scope->popInFunctionCall();
509: }
510:
511: if ($originalArg->name !== null) {
512: $index = $originalArg->name->toString();
513: $hasName = true;
514: } else {
515: $index = $i;
516: }
517: if ($originalArg->unpack) {
518: $unpack = true;
519: $types[$index] = $type->getIterableValueType();
520: } else {
521: $types[$index] = $type;
522: }
523: }
524:
525: if ($hasName && $namedArgumentsVariants !== null) {
526: return self::selectFromTypes($types, $namedArgumentsVariants, $unpack);
527: }
528:
529: return self::selectFromTypes($types, $parametersAcceptors, $unpack);
530: }
531:
532: private static function hasAcceptorTemplateOrLateResolvableType(ParametersAcceptor $acceptor): bool
533: {
534: if (self::hasTemplateOrLateResolvableType($acceptor->getReturnType())) {
535: return true;
536: }
537:
538: foreach ($acceptor->getParameters() as $parameter) {
539: if (
540: $parameter instanceof ExtendedParameterReflection
541: && $parameter->getOutType() !== null
542: && self::hasTemplateOrLateResolvableType($parameter->getOutType())
543: ) {
544: return true;
545: }
546:
547: if (
548: $parameter instanceof ExtendedParameterReflection
549: && $parameter->getClosureThisType() !== null
550: && self::hasTemplateOrLateResolvableType($parameter->getClosureThisType())
551: ) {
552: return true;
553: }
554:
555: if (!self::hasTemplateOrLateResolvableType($parameter->getType())) {
556: continue;
557: }
558:
559: return true;
560: }
561:
562: return false;
563: }
564:
565: private static function hasTemplateOrLateResolvableType(Type $type): bool
566: {
567: $has = false;
568: TypeTraverser::map($type, static function (Type $type, callable $traverse) use (&$has): Type {
569: if ($type instanceof TemplateType || $type instanceof LateResolvableType) {
570: $has = true;
571: return $type;
572: }
573:
574: return $traverse($type);
575: });
576:
577: return $has;
578: }
579:
580: /**
581: * @param array<int|string, Type> $types
582: * @param ParametersAcceptor[] $parametersAcceptors
583: */
584: public static function selectFromTypes(
585: array $types,
586: array $parametersAcceptors,
587: bool $unpack,
588: ): ParametersAcceptor
589: {
590: if (count($parametersAcceptors) === 1) {
591: return GenericParametersAcceptorResolver::resolve($types, $parametersAcceptors[0]);
592: }
593:
594: if (count($parametersAcceptors) === 0) {
595: throw new ShouldNotHappenException(
596: 'getVariants() must return at least one variant.',
597: );
598: }
599:
600: $typesCount = count($types);
601: $acceptableAcceptors = [];
602:
603: foreach ($parametersAcceptors as $parametersAcceptor) {
604: if ($unpack) {
605: $acceptableAcceptors[] = $parametersAcceptor;
606: continue;
607: }
608:
609: $functionParametersMinCount = 0;
610: $functionParametersMaxCount = 0;
611: foreach ($parametersAcceptor->getParameters() as $parameter) {
612: if (!$parameter->isOptional()) {
613: $functionParametersMinCount++;
614: }
615:
616: $functionParametersMaxCount++;
617: }
618:
619: if ($typesCount < $functionParametersMinCount) {
620: continue;
621: }
622:
623: if (
624: !$parametersAcceptor->isVariadic()
625: && $typesCount > $functionParametersMaxCount
626: ) {
627: continue;
628: }
629:
630: $acceptableAcceptors[] = $parametersAcceptor;
631: }
632:
633: if (count($acceptableAcceptors) === 0) {
634: return GenericParametersAcceptorResolver::resolve($types, self::combineAcceptors($parametersAcceptors));
635: }
636:
637: if (count($acceptableAcceptors) === 1) {
638: return GenericParametersAcceptorResolver::resolve($types, $acceptableAcceptors[0]);
639: }
640:
641: $winningAcceptors = [];
642: $winningCertainty = null;
643: foreach ($acceptableAcceptors as $acceptableAcceptor) {
644: $isSuperType = TrinaryLogic::createYes();
645: $acceptableAcceptor = GenericParametersAcceptorResolver::resolve($types, $acceptableAcceptor);
646: foreach ($acceptableAcceptor->getParameters() as $i => $parameter) {
647: if (!isset($types[$i])) {
648: if (!$unpack || count($types) <= 0) {
649: break;
650: }
651:
652: $type = $types[array_key_last($types)];
653: } else {
654: $type = $types[$i];
655: }
656:
657: if ($parameter->getType() instanceof MixedType) {
658: $isSuperType = $isSuperType->and(TrinaryLogic::createMaybe());
659: } else {
660: $isSuperType = $isSuperType->and($parameter->getType()->isSuperTypeOf($type)->result);
661: }
662: }
663:
664: if ($isSuperType->no()) {
665: continue;
666: }
667:
668: if ($winningCertainty === null) {
669: $winningAcceptors[] = $acceptableAcceptor;
670: $winningCertainty = $isSuperType;
671: } else {
672: $comparison = $winningCertainty->compareTo($isSuperType);
673: if ($comparison === $isSuperType) {
674: $winningAcceptors = [$acceptableAcceptor];
675: $winningCertainty = $isSuperType;
676: } elseif ($comparison === null) {
677: $winningAcceptors[] = $acceptableAcceptor;
678: }
679: }
680: }
681:
682: if (count($winningAcceptors) === 0) {
683: return GenericParametersAcceptorResolver::resolve($types, self::combineAcceptors($acceptableAcceptors));
684: }
685:
686: return GenericParametersAcceptorResolver::resolve($types, self::combineAcceptors($winningAcceptors));
687: }
688:
689: /**
690: * @param ParametersAcceptor[] $acceptors
691: */
692: public static function combineAcceptors(array $acceptors): ExtendedParametersAcceptor
693: {
694: if (count($acceptors) === 0) {
695: throw new ShouldNotHappenException(
696: 'getVariants() must return at least one variant.',
697: );
698: }
699: if (count($acceptors) === 1) {
700: return self::wrapAcceptor($acceptors[0]);
701: }
702:
703: $minimumNumberOfParameters = null;
704: foreach ($acceptors as $acceptor) {
705: $acceptorParametersMinCount = 0;
706: foreach ($acceptor->getParameters() as $parameter) {
707: if ($parameter->isOptional()) {
708: continue;
709: }
710:
711: $acceptorParametersMinCount++;
712: }
713:
714: if ($minimumNumberOfParameters !== null && $minimumNumberOfParameters <= $acceptorParametersMinCount) {
715: continue;
716: }
717:
718: $minimumNumberOfParameters = $acceptorParametersMinCount;
719: }
720:
721: $parameters = [];
722: $isVariadic = false;
723: $returnTypes = [];
724: $phpDocReturnTypes = [];
725: $nativeReturnTypes = [];
726: $callableOccurred = false;
727: $throwPoints = [];
728: $isPure = TrinaryLogic::createNo();
729: $impurePoints = [];
730: $invalidateExpressions = [];
731: $usedVariables = [];
732: $acceptsNamedArguments = TrinaryLogic::createNo();
733: $mustUseReturnValue = 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: }
752: $isVariadic = $isVariadic || $acceptor->isVariadic();
753:
754: foreach ($acceptor->getParameters() as $i => $parameter) {
755: if (!isset($parameters[$i])) {
756: $parameters[$i] = new ExtendedDummyParameter(
757: $parameter->getName(),
758: $parameter->getType(),
759: $i + 1 > $minimumNumberOfParameters,
760: $parameter->passedByReference(),
761: $parameter->isVariadic(),
762: $parameter->getDefaultValue(),
763: $parameter instanceof ExtendedParameterReflection ? $parameter->getNativeType() : new MixedType(),
764: $parameter instanceof ExtendedParameterReflection ? $parameter->getPhpDocType() : new MixedType(),
765: $parameter instanceof ExtendedParameterReflection ? $parameter->getOutType() : null,
766: $parameter instanceof ExtendedParameterReflection ? $parameter->isImmediatelyInvokedCallable() : TrinaryLogic::createMaybe(),
767: $parameter instanceof ExtendedParameterReflection ? $parameter->getClosureThisType() : null,
768: $parameter instanceof ExtendedParameterReflection ? $parameter->getAttributes() : [],
769: );
770: continue;
771: }
772:
773: $isVariadic = $parameters[$i]->isVariadic() || $parameter->isVariadic();
774: $defaultValueLeft = $parameters[$i]->getDefaultValue();
775: $defaultValueRight = $parameter->getDefaultValue();
776: if ($defaultValueLeft !== null && $defaultValueRight !== null) {
777: $defaultValue = TypeCombinator::union($defaultValueLeft, $defaultValueRight);
778: } else {
779: $defaultValue = null;
780: }
781:
782: $type = TypeCombinator::union($parameters[$i]->getType(), $parameter->getType());
783: $nativeType = $parameters[$i]->getNativeType();
784: $phpDocType = $parameters[$i]->getPhpDocType();
785: $outType = $parameters[$i]->getOutType();
786: $immediatelyInvokedCallable = $parameters[$i]->isImmediatelyInvokedCallable();
787: $closureThisType = $parameters[$i]->getClosureThisType();
788: $attributes = $parameters[$i]->getAttributes();
789: if ($parameter instanceof ExtendedParameterReflection) {
790: $nativeType = TypeCombinator::union($nativeType, $parameter->getNativeType());
791: $phpDocType = TypeCombinator::union($phpDocType, $parameter->getPhpDocType());
792:
793: if ($parameter->getOutType() !== null) {
794: $outType = $outType === null ? null : TypeCombinator::union($outType, $parameter->getOutType());
795: } else {
796: $outType = null;
797: }
798:
799: if ($parameter->getClosureThisType() !== null && $closureThisType !== null) {
800: $closureThisType = TypeCombinator::union($closureThisType, $parameter->getClosureThisType());
801: } else {
802: $closureThisType = null;
803: }
804:
805: $immediatelyInvokedCallable = $parameter->isImmediatelyInvokedCallable()->or($immediatelyInvokedCallable);
806: $attributes = array_merge($attributes, $parameter->getAttributes());
807: } else {
808: $nativeType = new MixedType();
809: $phpDocType = $type;
810: $outType = null;
811: $immediatelyInvokedCallable = TrinaryLogic::createMaybe();
812: $closureThisType = null;
813: }
814:
815: $parameters[$i] = new ExtendedDummyParameter(
816: $parameters[$i]->getName() !== $parameter->getName() ? sprintf('%s|%s', $parameters[$i]->getName(), $parameter->getName()) : $parameter->getName(),
817: $type,
818: $i + 1 > $minimumNumberOfParameters,
819: $parameters[$i]->passedByReference()->combine($parameter->passedByReference()),
820: $isVariadic,
821: $defaultValue,
822: $nativeType,
823: $phpDocType,
824: $outType,
825: $immediatelyInvokedCallable,
826: $closureThisType,
827: $attributes,
828: );
829:
830: if ($isVariadic) {
831: $parameters = array_slice($parameters, 0, $i + 1);
832: break;
833: }
834: }
835: }
836:
837: $returnType = TypeCombinator::union(...$returnTypes);
838: $phpDocReturnType = $phpDocReturnTypes === [] ? null : TypeCombinator::union(...$phpDocReturnTypes);
839: $nativeReturnType = $nativeReturnTypes === [] ? null : TypeCombinator::union(...$nativeReturnTypes);
840:
841: if ($callableOccurred) {
842: return new ExtendedCallableFunctionVariant(
843: TemplateTypeMap::createEmpty(),
844: null,
845: array_values($parameters),
846: $isVariadic,
847: $returnType,
848: $phpDocReturnType ?? $returnType,
849: $nativeReturnType ?? new MixedType(),
850: null,
851: $throwPoints,
852: $isPure,
853: $impurePoints,
854: $invalidateExpressions,
855: $usedVariables,
856: $acceptsNamedArguments,
857: $mustUseReturnValue,
858: );
859: }
860:
861: return new ExtendedFunctionVariant(
862: TemplateTypeMap::createEmpty(),
863: null,
864: array_values($parameters),
865: $isVariadic,
866: $returnType,
867: $phpDocReturnType ?? $returnType,
868: $nativeReturnType ?? new MixedType(),
869: );
870: }
871:
872: private static function wrapAcceptor(ParametersAcceptor $acceptor): ExtendedParametersAcceptor
873: {
874: if ($acceptor instanceof ExtendedParametersAcceptor) {
875: return $acceptor;
876: }
877:
878: if ($acceptor instanceof CallableParametersAcceptor) {
879: return new ExtendedCallableFunctionVariant(
880: $acceptor->getTemplateTypeMap(),
881: $acceptor->getResolvedTemplateTypeMap(),
882: array_map(static fn (ParameterReflection $parameter): ExtendedParameterReflection => self::wrapParameter($parameter), $acceptor->getParameters()),
883: $acceptor->isVariadic(),
884: $acceptor->getReturnType(),
885: $acceptor->getReturnType(),
886: new MixedType(),
887: TemplateTypeVarianceMap::createEmpty(),
888: $acceptor->getThrowPoints(),
889: $acceptor->isPure(),
890: $acceptor->getImpurePoints(),
891: $acceptor->getInvalidateExpressions(),
892: $acceptor->getUsedVariables(),
893: $acceptor->acceptsNamedArguments(),
894: $acceptor->mustUseReturnValue(),
895: );
896: }
897:
898: return new ExtendedFunctionVariant(
899: $acceptor->getTemplateTypeMap(),
900: $acceptor->getResolvedTemplateTypeMap(),
901: array_map(static fn (ParameterReflection $parameter): ExtendedParameterReflection => self::wrapParameter($parameter), $acceptor->getParameters()),
902: $acceptor->isVariadic(),
903: $acceptor->getReturnType(),
904: $acceptor->getReturnType(),
905: new MixedType(),
906: TemplateTypeVarianceMap::createEmpty(),
907: );
908: }
909:
910: private static function wrapParameter(ParameterReflection $parameter): ExtendedParameterReflection
911: {
912: return $parameter instanceof ExtendedParameterReflection ? $parameter : new ExtendedDummyParameter(
913: $parameter->getName(),
914: $parameter->getType(),
915: $parameter->isOptional(),
916: $parameter->passedByReference(),
917: $parameter->isVariadic(),
918: $parameter->getDefaultValue(),
919: new MixedType(),
920: $parameter->getType(),
921: null,
922: TrinaryLogic::createMaybe(),
923: null,
924: [],
925: );
926: }
927:
928: private static function getCurlOptValueType(int $curlOpt): ?Type
929: {
930: if (defined('CURLOPT_SSL_VERIFYHOST') && $curlOpt === CURLOPT_SSL_VERIFYHOST) {
931: return new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(2)]);
932: }
933:
934: $boolConstants = [
935: 'CURLOPT_AUTOREFERER',
936: 'CURLOPT_COOKIESESSION',
937: 'CURLOPT_CERTINFO',
938: 'CURLOPT_CONNECT_ONLY',
939: 'CURLOPT_CRLF',
940: 'CURLOPT_DISALLOW_USERNAME_IN_URL',
941: 'CURLOPT_DNS_SHUFFLE_ADDRESSES',
942: 'CURLOPT_HAPROXYPROTOCOL',
943: 'CURLOPT_SSH_COMPRESSION',
944: 'CURLOPT_DNS_USE_GLOBAL_CACHE',
945: 'CURLOPT_FAILONERROR',
946: 'CURLOPT_SSL_FALSESTART',
947: 'CURLOPT_FILETIME',
948: 'CURLOPT_FOLLOWLOCATION',
949: 'CURLOPT_FORBID_REUSE',
950: 'CURLOPT_FRESH_CONNECT',
951: 'CURLOPT_FTP_USE_EPRT',
952: 'CURLOPT_FTP_USE_EPSV',
953: 'CURLOPT_FTP_CREATE_MISSING_DIRS',
954: 'CURLOPT_FTPAPPEND',
955: 'CURLOPT_TCP_NODELAY',
956: 'CURLOPT_FTPASCII',
957: 'CURLOPT_FTPLISTONLY',
958: 'CURLOPT_HEADER',
959: 'CURLOPT_HTTP09_ALLOWED',
960: 'CURLOPT_HTTPGET',
961: 'CURLOPT_HTTPPROXYTUNNEL',
962: 'CURLOPT_HTTP_CONTENT_DECODING',
963: 'CURLOPT_KEEP_SENDING_ON_ERROR',
964: 'CURLOPT_MUTE',
965: 'CURLOPT_NETRC',
966: 'CURLOPT_NOBODY',
967: 'CURLOPT_NOPROGRESS',
968: 'CURLOPT_NOSIGNAL',
969: 'CURLOPT_PATH_AS_IS',
970: 'CURLOPT_PIPEWAIT',
971: 'CURLOPT_POST',
972: 'CURLOPT_PUT',
973: 'CURLOPT_RETURNTRANSFER',
974: 'CURLOPT_SASL_IR',
975: 'CURLOPT_SSL_ENABLE_ALPN',
976: 'CURLOPT_SSL_ENABLE_NPN',
977: 'CURLOPT_SSL_VERIFYPEER',
978: 'CURLOPT_SSL_VERIFYSTATUS',
979: 'CURLOPT_PROXY_SSL_VERIFYPEER',
980: 'CURLOPT_SUPPRESS_CONNECT_HEADERS',
981: 'CURLOPT_TCP_FASTOPEN',
982: 'CURLOPT_TFTP_NO_OPTIONS',
983: 'CURLOPT_TRANSFERTEXT',
984: 'CURLOPT_UNRESTRICTED_AUTH',
985: 'CURLOPT_UPLOAD',
986: 'CURLOPT_VERBOSE',
987: ];
988: foreach ($boolConstants as $constName) {
989: if (defined($constName) && constant($constName) === $curlOpt) {
990: return new BooleanType();
991: }
992: }
993:
994: $intConstants = [
995: 'CURLOPT_BUFFERSIZE',
996: 'CURLOPT_CONNECTTIMEOUT',
997: 'CURLOPT_CONNECTTIMEOUT_MS',
998: 'CURLOPT_DNS_CACHE_TIMEOUT',
999: 'CURLOPT_EXPECT_100_TIMEOUT_MS',
1000: 'CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS',
1001: 'CURLOPT_FTPSSLAUTH',
1002: 'CURLOPT_HEADEROPT',
1003: 'CURLOPT_HTTP_VERSION',
1004: 'CURLOPT_HTTPAUTH',
1005: 'CURLOPT_INFILESIZE',
1006: 'CURLOPT_LOW_SPEED_LIMIT',
1007: 'CURLOPT_LOW_SPEED_TIME',
1008: 'CURLOPT_MAXCONNECTS',
1009: 'CURLOPT_MAXREDIRS',
1010: 'CURLOPT_PORT',
1011: 'CURLOPT_POSTREDIR',
1012: 'CURLOPT_PROTOCOLS',
1013: 'CURLOPT_PROXYAUTH',
1014: 'CURLOPT_PROXYPORT',
1015: 'CURLOPT_PROXYTYPE',
1016: 'CURLOPT_REDIR_PROTOCOLS',
1017: 'CURLOPT_RESUME_FROM',
1018: 'CURLOPT_SOCKS5_AUTH',
1019: 'CURLOPT_SSL_OPTIONS',
1020: 'CURLOPT_SSL_VERIFYHOST',
1021: 'CURLOPT_SSLVERSION',
1022: 'CURLOPT_PROXY_SSL_OPTIONS',
1023: 'CURLOPT_PROXY_SSL_VERIFYHOST',
1024: 'CURLOPT_PROXY_SSLVERSION',
1025: 'CURLOPT_STREAM_WEIGHT',
1026: 'CURLOPT_TCP_KEEPALIVE',
1027: 'CURLOPT_TCP_KEEPIDLE',
1028: 'CURLOPT_TCP_KEEPINTVL',
1029: 'CURLOPT_TIMECONDITION',
1030: 'CURLOPT_TIMEOUT',
1031: 'CURLOPT_TIMEOUT_MS',
1032: 'CURLOPT_TIMEVALUE',
1033: 'CURLOPT_TIMEVALUE_LARGE',
1034: 'CURLOPT_MAX_RECV_SPEED_LARGE',
1035: 'CURLOPT_SSH_AUTH_TYPES',
1036: 'CURLOPT_IPRESOLVE',
1037: 'CURLOPT_FTP_FILEMETHOD',
1038: ];
1039: foreach ($intConstants as $constName) {
1040: if (defined($constName) && constant($constName) === $curlOpt) {
1041: return new IntegerType();
1042: }
1043: }
1044:
1045: $nullableStringConstants = [
1046: 'CURLOPT_CUSTOMREQUEST',
1047: 'CURLOPT_DNS_INTERFACE',
1048: 'CURLOPT_DNS_LOCAL_IP4',
1049: 'CURLOPT_DNS_LOCAL_IP6',
1050: 'CURLOPT_DOH_URL',
1051: 'CURLOPT_FTP_ACCOUNT',
1052: 'CURLOPT_FTPPORT',
1053: 'CURLOPT_HSTS',
1054: 'CURLOPT_KRBLEVEL',
1055: 'CURLOPT_RANGE',
1056: 'CURLOPT_RTSP_SESSION_ID',
1057: 'CURLOPT_UNIX_SOCKET_PATH',
1058: 'CURLOPT_XOAUTH2_BEARER',
1059: ];
1060: foreach ($nullableStringConstants as $constName) {
1061: if (defined($constName) && constant($constName) === $curlOpt) {
1062: return new UnionType([
1063: new NullType(),
1064: TypeCombinator::intersect(
1065: new StringType(),
1066: new AccessoryNonEmptyStringType(),
1067: ),
1068: ]);
1069: }
1070: }
1071:
1072: $nonEmptyStringConstants = [
1073: 'CURLOPT_ABSTRACT_UNIX_SOCKET',
1074: 'CURLOPT_ALTSVC',
1075: 'CURLOPT_AWS_SIGV4',
1076: 'CURLOPT_CAINFO',
1077: 'CURLOPT_CAPATH',
1078: 'CURLOPT_COOKIE',
1079: 'CURLOPT_COOKIEJAR',
1080: 'CURLOPT_COOKIELIST',
1081: 'CURLOPT_DEFAULT_PROTOCOL',
1082: 'CURLOPT_DNS_SERVERS',
1083: 'CURLOPT_EGDSOCKET',
1084: 'CURLOPT_FTP_ALTERNATIVE_TO_USER',
1085: 'CURLOPT_INTERFACE',
1086: 'CURLOPT_KEYPASSWD',
1087: 'CURLOPT_KRB4LEVEL',
1088: 'CURLOPT_LOGIN_OPTIONS',
1089: 'CURLOPT_MAIL_AUTH',
1090: 'CURLOPT_MAIL_FROM',
1091: 'CURLOPT_NOPROXY',
1092: 'CURLOPT_PASSWORD',
1093: 'CURLOPT_PINNEDPUBLICKEY',
1094: 'CURLOPT_PROTOCOLS_STR',
1095: 'CURLOPT_PROXY_CAINFO',
1096: 'CURLOPT_PROXY_CAPATH',
1097: 'CURLOPT_PROXY_CRLFILE',
1098: 'CURLOPT_PROXY_ISSUERCERT',
1099: 'CURLOPT_PROXY_KEYPASSWD',
1100: 'CURLOPT_PROXY_PINNEDPUBLICKEY',
1101: 'CURLOPT_PROXY_SERVICE_NAME',
1102: 'CURLOPT_PROXY_SSL_CIPHER_LIST',
1103: 'CURLOPT_PROXY_SSLCERT',
1104: 'CURLOPT_PROXY_SSLCERTTYPE',
1105: 'CURLOPT_PROXY_SSLKEY',
1106: 'CURLOPT_PROXY_SSLKEYTYPE',
1107: 'CURLOPT_PROXY_TLS13_CIPHERS',
1108: 'CURLOPT_PROXY_TLSAUTH_PASSWORD',
1109: 'CURLOPT_PROXY_TLSAUTH_TYPE',
1110: 'CURLOPT_PROXY_TLSAUTH_USERNAME',
1111: 'CURLOPT_PROXYPASSWORD',
1112: 'CURLOPT_PROXYUSERNAME',
1113: 'CURLOPT_PROXYUSERPWD',
1114: 'CURLOPT_RANDOM_FILE',
1115: 'CURLOPT_REDIR_PROTOCOLS_STR',
1116: 'CURLOPT_REFERER',
1117: 'CURLOPT_REQUEST_TARGET',
1118: 'CURLOPT_RTSP_STREAM_URI',
1119: 'CURLOPT_RTSP_TRANSPORT',
1120: 'CURLOPT_SASL_AUTHZID',
1121: 'CURLOPT_SERVICE_NAME',
1122: 'CURLOPT_SOCKS5_GSSAPI_SERVICE',
1123: 'CURLOPT_SSH_HOST_PUBLIC_KEY_MD5',
1124: 'CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256',
1125: 'CURLOPT_SSH_PRIVATE_KEYFILE',
1126: 'CURLOPT_SSH_PUBLIC_KEYFILE',
1127: 'CURLOPT_SSL_CIPHER_LIST',
1128: 'CURLOPT_SSL_EC_CURVES',
1129: 'CURLOPT_SSLCERT',
1130: 'CURLOPT_SSLCERTPASSWD',
1131: 'CURLOPT_SSLCERTTYPE',
1132: 'CURLOPT_SSLENGINE',
1133: 'CURLOPT_SSLENGINE_DEFAULT',
1134: 'CURLOPT_SSLKEY',
1135: 'CURLOPT_SSLKEYPASSWD',
1136: 'CURLOPT_SSLKEYTYPE',
1137: 'CURLOPT_TLS13_CIPHERS',
1138: 'CURLOPT_TLSAUTH_PASSWORD',
1139: 'CURLOPT_TLSAUTH_TYPE',
1140: 'CURLOPT_TLSAUTH_USERNAME',
1141: 'CURLOPT_TRANSFER_ENCODING',
1142: 'CURLOPT_URL',
1143: 'CURLOPT_USERAGENT',
1144: 'CURLOPT_USERNAME',
1145: 'CURLOPT_USERPWD',
1146: ];
1147: foreach ($nonEmptyStringConstants as $constName) {
1148: if (defined($constName) && constant($constName) === $curlOpt) {
1149: return TypeCombinator::intersect(
1150: new StringType(),
1151: new AccessoryNonEmptyStringType(),
1152: );
1153: }
1154: }
1155:
1156: $stringConstants = [
1157: 'CURLOPT_COOKIEFILE',
1158: 'CURLOPT_ENCODING', // Alias: CURLOPT_ACCEPT_ENCODING
1159: 'CURLOPT_PRE_PROXY',
1160: 'CURLOPT_PRIVATE',
1161: 'CURLOPT_PROXY',
1162: ];
1163: foreach ($stringConstants as $constName) {
1164: if (defined($constName) && constant($constName) === $curlOpt) {
1165: return new StringType();
1166: }
1167: }
1168:
1169: $intArrayStringKeysConstants = [
1170: 'CURLOPT_HTTPHEADER',
1171: ];
1172: foreach ($intArrayStringKeysConstants as $constName) {
1173: if (defined($constName) && constant($constName) === $curlOpt) {
1174: return new ArrayType(new IntegerType(), new StringType());
1175: }
1176: }
1177:
1178: $arrayConstants = [
1179: 'CURLOPT_CONNECT_TO',
1180: 'CURLOPT_HTTP200ALIASES',
1181: 'CURLOPT_POSTQUOTE',
1182: 'CURLOPT_PROXYHEADER',
1183: 'CURLOPT_QUOTE',
1184: 'CURLOPT_RESOLVE',
1185: ];
1186: foreach ($arrayConstants as $constName) {
1187: if (defined($constName) && constant($constName) === $curlOpt) {
1188: return new ArrayType(new MixedType(), new MixedType());
1189: }
1190: }
1191:
1192: $arrayOrStringConstants = [
1193: 'CURLOPT_POSTFIELDS',
1194: ];
1195: foreach ($arrayOrStringConstants as $constName) {
1196: if (defined($constName) && constant($constName) === $curlOpt) {
1197: return new UnionType([
1198: new StringType(),
1199: new ArrayType(new MixedType(), new MixedType()),
1200: ]);
1201: }
1202: }
1203:
1204: $resourceConstants = [
1205: 'CURLOPT_FILE',
1206: 'CURLOPT_INFILE',
1207: 'CURLOPT_STDERR',
1208: 'CURLOPT_WRITEHEADER',
1209: ];
1210: foreach ($resourceConstants as $constName) {
1211: if (defined($constName) && constant($constName) === $curlOpt) {
1212: return new ResourceType();
1213: }
1214: }
1215:
1216: if ($curlOpt === CURLOPT_SHARE) {
1217: $phpversion = PhpVersionStaticAccessor::getInstance();
1218:
1219: if ($phpversion->supportsCurlShareHandle()) {
1220: $shareType = new ObjectType('CurlShareHandle');
1221: } else {
1222: $shareType = new ResourceType();
1223: }
1224: if ($phpversion->supportsCurlSharePersistentHandle()) {
1225: $shareType = TypeCombinator::union($shareType, new ObjectType('CurlSharePersistentHandle'));
1226: }
1227:
1228: return $shareType;
1229: }
1230:
1231: // unknown constant
1232: return null;
1233: }
1234:
1235: }
1236: