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