1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Reflection;
4:
5: use PhpParser\Node;
6: use PHPStan\Analyser\Scope;
7: use PHPStan\Parser\ArrayFilterArgVisitor;
8: use PHPStan\Parser\ArrayMapArgVisitor;
9: use PHPStan\Parser\ArrayWalkArgVisitor;
10: use PHPStan\Parser\CurlSetOptArgVisitor;
11: use PHPStan\Reflection\Native\NativeParameterReflection;
12: use PHPStan\Reflection\Php\DummyParameter;
13: use PHPStan\Reflection\Php\DummyParameterWithPhpDocs;
14: use PHPStan\ShouldNotHappenException;
15: use PHPStan\TrinaryLogic;
16: use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
17: use PHPStan\Type\ArrayType;
18: use PHPStan\Type\BooleanType;
19: use PHPStan\Type\CallableType;
20: use PHPStan\Type\Constant\ConstantIntegerType;
21: use PHPStan\Type\Generic\TemplateType;
22: use PHPStan\Type\Generic\TemplateTypeMap;
23: use PHPStan\Type\Generic\TemplateTypeVarianceMap;
24: use PHPStan\Type\IntegerType;
25: use PHPStan\Type\LateResolvableType;
26: use PHPStan\Type\MixedType;
27: use PHPStan\Type\NullType;
28: use PHPStan\Type\ResourceType;
29: use PHPStan\Type\StringType;
30: use PHPStan\Type\Type;
31: use PHPStan\Type\TypeCombinator;
32: use PHPStan\Type\TypeTraverser;
33: use PHPStan\Type\UnionType;
34: use function array_key_last;
35: use function array_map;
36: use function array_slice;
37: use function constant;
38: use function count;
39: use function defined;
40: use function sprintf;
41: use const ARRAY_FILTER_USE_BOTH;
42: use const ARRAY_FILTER_USE_KEY;
43: use const CURLOPT_SSL_VERIFYHOST;
44:
45: /** @api */
46: class ParametersAcceptorSelector
47: {
48:
49: /**
50: * @template T of ParametersAcceptor
51: * @param T[] $parametersAcceptors
52: * @return T
53: */
54: public static function selectSingle(
55: array $parametersAcceptors,
56: ): ParametersAcceptor
57: {
58: $count = count($parametersAcceptors);
59: if ($count === 0) {
60: throw new ShouldNotHappenException(
61: 'getVariants() must return at least one variant.',
62: );
63: }
64: if ($count !== 1) {
65: throw new ShouldNotHappenException('Multiple variants - use selectFromArgs() instead.');
66: }
67:
68: return $parametersAcceptors[0];
69: }
70:
71: /**
72: * @param Node\Arg[] $args
73: * @param ParametersAcceptor[] $parametersAcceptors
74: * @param ParametersAcceptor[]|null $namedArgumentsVariants
75: */
76: public static function selectFromArgs(
77: Scope $scope,
78: array $args,
79: array $parametersAcceptors,
80: ?array $namedArgumentsVariants = null,
81: ): ParametersAcceptor
82: {
83: $types = [];
84: $unpack = false;
85: if (
86: count($args) > 0
87: && count($parametersAcceptors) > 0
88: ) {
89: $arrayMapArgs = $args[0]->value->getAttribute(ArrayMapArgVisitor::ATTRIBUTE_NAME);
90: if ($arrayMapArgs !== null) {
91: $acceptor = $parametersAcceptors[0];
92: $parameters = $acceptor->getParameters();
93: $callbackParameters = [];
94: foreach ($arrayMapArgs as $arg) {
95: $callbackParameters[] = new DummyParameter('item', $scope->getIterableValueType($scope->getType($arg->value)), false, PassedByReference::createNo(), false, null);
96: }
97: $parameters[0] = new NativeParameterReflection(
98: $parameters[0]->getName(),
99: $parameters[0]->isOptional(),
100: new UnionType([
101: new CallableType($callbackParameters, new MixedType(), false),
102: new NullType(),
103: ]),
104: $parameters[0]->passedByReference(),
105: $parameters[0]->isVariadic(),
106: $parameters[0]->getDefaultValue(),
107: );
108: $parametersAcceptors = [
109: new FunctionVariant(
110: $acceptor->getTemplateTypeMap(),
111: $acceptor->getResolvedTemplateTypeMap(),
112: $parameters,
113: $acceptor->isVariadic(),
114: $acceptor->getReturnType(),
115: $acceptor instanceof ParametersAcceptorWithPhpDocs ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(),
116: ),
117: ];
118: }
119:
120: if (count($args) >= 3 && (bool) $args[0]->getAttribute(CurlSetOptArgVisitor::ATTRIBUTE_NAME)) {
121: $optType = $scope->getType($args[1]->value);
122: if ($optType instanceof ConstantIntegerType) {
123: $optValueType = self::getCurlOptValueType($optType->getValue());
124:
125: if ($optValueType !== null) {
126: $acceptor = $parametersAcceptors[0];
127: $parameters = $acceptor->getParameters();
128:
129: $parameters[2] = new NativeParameterReflection(
130: $parameters[2]->getName(),
131: $parameters[2]->isOptional(),
132: $optValueType,
133: $parameters[2]->passedByReference(),
134: $parameters[2]->isVariadic(),
135: $parameters[2]->getDefaultValue(),
136: );
137:
138: $parametersAcceptors = [
139: new FunctionVariant(
140: $acceptor->getTemplateTypeMap(),
141: $acceptor->getResolvedTemplateTypeMap(),
142: $parameters,
143: $acceptor->isVariadic(),
144: $acceptor->getReturnType(),
145: $acceptor instanceof ParametersAcceptorWithPhpDocs ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(),
146: ),
147: ];
148: }
149: }
150: }
151:
152: if (isset($args[0]) && (bool) $args[0]->getAttribute(ArrayFilterArgVisitor::ATTRIBUTE_NAME)) {
153: if (isset($args[2])) {
154: $mode = $scope->getType($args[2]->value);
155: if ($mode instanceof ConstantIntegerType) {
156: if ($mode->getValue() === ARRAY_FILTER_USE_KEY) {
157: $arrayFilterParameters = [
158: new DummyParameter('key', $scope->getIterableKeyType($scope->getType($args[0]->value)), false, PassedByReference::createNo(), false, null),
159: ];
160: } elseif ($mode->getValue() === ARRAY_FILTER_USE_BOTH) {
161: $arrayFilterParameters = [
162: new DummyParameter('item', $scope->getIterableValueType($scope->getType($args[0]->value)), false, PassedByReference::createNo(), false, null),
163: new DummyParameter('key', $scope->getIterableKeyType($scope->getType($args[0]->value)), false, PassedByReference::createNo(), false, null),
164: ];
165: }
166: }
167: }
168:
169: $acceptor = $parametersAcceptors[0];
170: $parameters = $acceptor->getParameters();
171: $parameters[1] = new NativeParameterReflection(
172: $parameters[1]->getName(),
173: $parameters[1]->isOptional(),
174: new UnionType([
175: new CallableType(
176: $arrayFilterParameters ?? [
177: new DummyParameter('item', $scope->getIterableValueType($scope->getType($args[0]->value)), false, PassedByReference::createNo(), false, null),
178: ],
179: new BooleanType(),
180: false,
181: ),
182: new NullType(),
183: ]),
184: $parameters[1]->passedByReference(),
185: $parameters[1]->isVariadic(),
186: $parameters[1]->getDefaultValue(),
187: );
188: $parametersAcceptors = [
189: new FunctionVariant(
190: $acceptor->getTemplateTypeMap(),
191: $acceptor->getResolvedTemplateTypeMap(),
192: $parameters,
193: $acceptor->isVariadic(),
194: $acceptor->getReturnType(),
195: $acceptor instanceof ParametersAcceptorWithPhpDocs ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(),
196: ),
197: ];
198: }
199:
200: if (isset($args[0]) && (bool) $args[0]->getAttribute(ArrayWalkArgVisitor::ATTRIBUTE_NAME)) {
201: $arrayWalkParameters = [
202: new DummyParameter('item', $scope->getIterableValueType($scope->getType($args[0]->value)), false, PassedByReference::createReadsArgument(), false, null),
203: new DummyParameter('key', $scope->getIterableKeyType($scope->getType($args[0]->value)), false, PassedByReference::createNo(), false, null),
204: ];
205: if (isset($args[2])) {
206: $arrayWalkParameters[] = new DummyParameter('arg', $scope->getType($args[2]->value), false, PassedByReference::createNo(), false, null);
207: }
208:
209: $acceptor = $parametersAcceptors[0];
210: $parameters = $acceptor->getParameters();
211: $parameters[1] = new NativeParameterReflection(
212: $parameters[1]->getName(),
213: $parameters[1]->isOptional(),
214: new CallableType($arrayWalkParameters, new MixedType(), false),
215: $parameters[1]->passedByReference(),
216: $parameters[1]->isVariadic(),
217: $parameters[1]->getDefaultValue(),
218: );
219: $parametersAcceptors = [
220: new FunctionVariant(
221: $acceptor->getTemplateTypeMap(),
222: $acceptor->getResolvedTemplateTypeMap(),
223: $parameters,
224: $acceptor->isVariadic(),
225: $acceptor->getReturnType(),
226: $acceptor instanceof ParametersAcceptorWithPhpDocs ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(),
227: ),
228: ];
229: }
230: }
231:
232: if (count($parametersAcceptors) === 1) {
233: $acceptor = $parametersAcceptors[0];
234: if (!self::hasAcceptorTemplateOrLateResolvableType($acceptor)) {
235: return $acceptor;
236: }
237: }
238:
239: $hasName = false;
240: foreach ($args as $i => $arg) {
241: $type = $scope->getType($arg->value);
242: if ($arg->name !== null) {
243: $index = $arg->name->toString();
244: $hasName = true;
245: } else {
246: $index = $i;
247: }
248: if ($arg->unpack) {
249: $unpack = true;
250: $types[$index] = $type->getIterableValueType();
251: } else {
252: $types[$index] = $type;
253: }
254: }
255:
256: if ($hasName && $namedArgumentsVariants !== null) {
257: return self::selectFromTypes($types, $namedArgumentsVariants, $unpack);
258: }
259:
260: return self::selectFromTypes($types, $parametersAcceptors, $unpack);
261: }
262:
263: private static function hasAcceptorTemplateOrLateResolvableType(ParametersAcceptor $acceptor): bool
264: {
265: if (self::hasTemplateOrLateResolvableType($acceptor->getReturnType())) {
266: return true;
267: }
268:
269: foreach ($acceptor->getParameters() as $parameter) {
270: if (!self::hasTemplateOrLateResolvableType($parameter->getType())) {
271: continue;
272: }
273:
274: return true;
275: }
276:
277: return false;
278: }
279:
280: private static function hasTemplateOrLateResolvableType(Type $type): bool
281: {
282: $has = false;
283: TypeTraverser::map($type, static function (Type $type, callable $traverse) use (&$has): Type {
284: if ($type instanceof TemplateType || $type instanceof LateResolvableType) {
285: $has = true;
286: return $type;
287: }
288:
289: return $traverse($type);
290: });
291:
292: return $has;
293: }
294:
295: /**
296: * @param array<int|string, Type> $types
297: * @param ParametersAcceptor[] $parametersAcceptors
298: */
299: public static function selectFromTypes(
300: array $types,
301: array $parametersAcceptors,
302: bool $unpack,
303: ): ParametersAcceptor
304: {
305: if (count($parametersAcceptors) === 1) {
306: return GenericParametersAcceptorResolver::resolve($types, $parametersAcceptors[0]);
307: }
308:
309: if (count($parametersAcceptors) === 0) {
310: throw new ShouldNotHappenException(
311: 'getVariants() must return at least one variant.',
312: );
313: }
314:
315: $typesCount = count($types);
316: $acceptableAcceptors = [];
317:
318: foreach ($parametersAcceptors as $parametersAcceptor) {
319: if ($unpack) {
320: $acceptableAcceptors[] = $parametersAcceptor;
321: continue;
322: }
323:
324: $functionParametersMinCount = 0;
325: $functionParametersMaxCount = 0;
326: foreach ($parametersAcceptor->getParameters() as $parameter) {
327: if (!$parameter->isOptional()) {
328: $functionParametersMinCount++;
329: }
330:
331: $functionParametersMaxCount++;
332: }
333:
334: if ($typesCount < $functionParametersMinCount) {
335: continue;
336: }
337:
338: if (
339: !$parametersAcceptor->isVariadic()
340: && $typesCount > $functionParametersMaxCount
341: ) {
342: continue;
343: }
344:
345: $acceptableAcceptors[] = $parametersAcceptor;
346: }
347:
348: if (count($acceptableAcceptors) === 0) {
349: return GenericParametersAcceptorResolver::resolve($types, self::combineAcceptors($parametersAcceptors));
350: }
351:
352: if (count($acceptableAcceptors) === 1) {
353: return GenericParametersAcceptorResolver::resolve($types, $acceptableAcceptors[0]);
354: }
355:
356: $winningAcceptors = [];
357: $winningCertainty = null;
358: foreach ($acceptableAcceptors as $acceptableAcceptor) {
359: $isSuperType = TrinaryLogic::createYes();
360: $acceptableAcceptor = GenericParametersAcceptorResolver::resolve($types, $acceptableAcceptor);
361: foreach ($acceptableAcceptor->getParameters() as $i => $parameter) {
362: if (!isset($types[$i])) {
363: if (!$unpack || count($types) <= 0) {
364: break;
365: }
366:
367: $type = $types[array_key_last($types)];
368: } else {
369: $type = $types[$i];
370: }
371:
372: if ($parameter->getType() instanceof MixedType) {
373: $isSuperType = $isSuperType->and(TrinaryLogic::createMaybe());
374: } else {
375: $isSuperType = $isSuperType->and($parameter->getType()->isSuperTypeOf($type));
376: }
377: }
378:
379: if ($isSuperType->no()) {
380: continue;
381: }
382:
383: if ($winningCertainty === null) {
384: $winningAcceptors[] = $acceptableAcceptor;
385: $winningCertainty = $isSuperType;
386: } else {
387: $comparison = $winningCertainty->compareTo($isSuperType);
388: if ($comparison === $isSuperType) {
389: $winningAcceptors = [$acceptableAcceptor];
390: $winningCertainty = $isSuperType;
391: } elseif ($comparison === null) {
392: $winningAcceptors[] = $acceptableAcceptor;
393: }
394: }
395: }
396:
397: if (count($winningAcceptors) === 0) {
398: return GenericParametersAcceptorResolver::resolve($types, self::combineAcceptors($acceptableAcceptors));
399: }
400:
401: return GenericParametersAcceptorResolver::resolve($types, self::combineAcceptors($winningAcceptors));
402: }
403:
404: /**
405: * @param ParametersAcceptor[] $acceptors
406: */
407: public static function combineAcceptors(array $acceptors): ParametersAcceptorWithPhpDocs
408: {
409: if (count($acceptors) === 0) {
410: throw new ShouldNotHappenException(
411: 'getVariants() must return at least one variant.',
412: );
413: }
414: if (count($acceptors) === 1) {
415: return self::wrapAcceptor($acceptors[0]);
416: }
417:
418: $minimumNumberOfParameters = null;
419: foreach ($acceptors as $acceptor) {
420: $acceptorParametersMinCount = 0;
421: foreach ($acceptor->getParameters() as $parameter) {
422: if ($parameter->isOptional()) {
423: continue;
424: }
425:
426: $acceptorParametersMinCount++;
427: }
428:
429: if ($minimumNumberOfParameters !== null && $minimumNumberOfParameters <= $acceptorParametersMinCount) {
430: continue;
431: }
432:
433: $minimumNumberOfParameters = $acceptorParametersMinCount;
434: }
435:
436: $parameters = [];
437: $isVariadic = false;
438: $returnTypes = [];
439: $phpDocReturnTypes = [];
440: $nativeReturnTypes = [];
441:
442: foreach ($acceptors as $acceptor) {
443: $returnTypes[] = $acceptor->getReturnType();
444:
445: if ($acceptor instanceof ParametersAcceptorWithPhpDocs) {
446: $phpDocReturnTypes[] = $acceptor->getPhpDocReturnType();
447: $nativeReturnTypes[] = $acceptor->getNativeReturnType();
448: }
449: $isVariadic = $isVariadic || $acceptor->isVariadic();
450:
451: foreach ($acceptor->getParameters() as $i => $parameter) {
452: if (!isset($parameters[$i])) {
453: $parameters[$i] = new DummyParameterWithPhpDocs(
454: $parameter->getName(),
455: $parameter->getType(),
456: $i + 1 > $minimumNumberOfParameters,
457: $parameter->passedByReference(),
458: $parameter->isVariadic(),
459: $parameter->getDefaultValue(),
460: $parameter instanceof ParameterReflectionWithPhpDocs ? $parameter->getNativeType() : new MixedType(),
461: $parameter instanceof ParameterReflectionWithPhpDocs ? $parameter->getPhpDocType() : new MixedType(),
462: $parameter instanceof ParameterReflectionWithPhpDocs ? $parameter->getOutType() : null,
463: );
464: continue;
465: }
466:
467: $isVariadic = $parameters[$i]->isVariadic() || $parameter->isVariadic();
468: $defaultValueLeft = $parameters[$i]->getDefaultValue();
469: $defaultValueRight = $parameter->getDefaultValue();
470: if ($defaultValueLeft !== null && $defaultValueRight !== null) {
471: $defaultValue = TypeCombinator::union($defaultValueLeft, $defaultValueRight);
472: } else {
473: $defaultValue = null;
474: }
475:
476: $type = TypeCombinator::union($parameters[$i]->getType(), $parameter->getType());
477: $nativeType = $parameters[$i]->getNativeType();
478: $phpDocType = $parameters[$i]->getPhpDocType();
479: $outType = $parameters[$i]->getOutType();
480: if ($parameter instanceof ParameterReflectionWithPhpDocs) {
481: $nativeType = TypeCombinator::union($nativeType, $parameter->getNativeType());
482: $phpDocType = TypeCombinator::union($phpDocType, $parameter->getPhpDocType());
483:
484: if ($parameter->getOutType() !== null) {
485: $outType = $outType === null ? null : TypeCombinator::union($outType, $parameter->getOutType());
486: } else {
487: $outType = null;
488: }
489: } else {
490: $nativeType = new MixedType();
491: $phpDocType = $type;
492: $outType = null;
493: }
494:
495: $parameters[$i] = new DummyParameterWithPhpDocs(
496: $parameters[$i]->getName() !== $parameter->getName() ? sprintf('%s|%s', $parameters[$i]->getName(), $parameter->getName()) : $parameter->getName(),
497: $type,
498: $i + 1 > $minimumNumberOfParameters,
499: $parameters[$i]->passedByReference()->combine($parameter->passedByReference()),
500: $isVariadic,
501: $defaultValue,
502: $nativeType,
503: $phpDocType,
504: $outType,
505: );
506:
507: if ($isVariadic) {
508: $parameters = array_slice($parameters, 0, $i + 1);
509: break;
510: }
511: }
512: }
513:
514: $returnType = TypeCombinator::union(...$returnTypes);
515: $phpDocReturnType = $phpDocReturnTypes === [] ? null : TypeCombinator::union(...$phpDocReturnTypes);
516: $nativeReturnType = $nativeReturnTypes === [] ? null : TypeCombinator::union(...$nativeReturnTypes);
517:
518: return new FunctionVariantWithPhpDocs(
519: TemplateTypeMap::createEmpty(),
520: null,
521: $parameters,
522: $isVariadic,
523: $returnType,
524: $phpDocReturnType ?? $returnType,
525: $nativeReturnType ?? new MixedType(),
526: );
527: }
528:
529: private static function wrapAcceptor(ParametersAcceptor $acceptor): ParametersAcceptorWithPhpDocs
530: {
531: if ($acceptor instanceof ParametersAcceptorWithPhpDocs) {
532: return $acceptor;
533: }
534:
535: return new FunctionVariantWithPhpDocs(
536: $acceptor->getTemplateTypeMap(),
537: $acceptor->getResolvedTemplateTypeMap(),
538: array_map(static fn (ParameterReflection $parameter): ParameterReflectionWithPhpDocs => self::wrapParameter($parameter), $acceptor->getParameters()),
539: $acceptor->isVariadic(),
540: $acceptor->getReturnType(),
541: $acceptor->getReturnType(),
542: new MixedType(),
543: TemplateTypeVarianceMap::createEmpty(),
544: );
545: }
546:
547: private static function wrapParameter(ParameterReflection $parameter): ParameterReflectionWithPhpDocs
548: {
549: return $parameter instanceof ParameterReflectionWithPhpDocs ? $parameter : new DummyParameterWithPhpDocs(
550: $parameter->getName(),
551: $parameter->getType(),
552: $parameter->isOptional(),
553: $parameter->passedByReference(),
554: $parameter->isVariadic(),
555: $parameter->getDefaultValue(),
556: new MixedType(),
557: $parameter->getType(),
558: null,
559: );
560: }
561:
562: private static function getCurlOptValueType(int $curlOpt): ?Type
563: {
564: if (defined('CURLOPT_SSL_VERIFYHOST') && $curlOpt === CURLOPT_SSL_VERIFYHOST) {
565: return new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(2)]);
566: }
567:
568: $boolConstants = [
569: 'CURLOPT_AUTOREFERER',
570: 'CURLOPT_COOKIESESSION',
571: 'CURLOPT_CERTINFO',
572: 'CURLOPT_CONNECT_ONLY',
573: 'CURLOPT_CRLF',
574: 'CURLOPT_DISALLOW_USERNAME_IN_URL',
575: 'CURLOPT_DNS_SHUFFLE_ADDRESSES',
576: 'CURLOPT_HAPROXYPROTOCOL',
577: 'CURLOPT_SSH_COMPRESSION',
578: 'CURLOPT_DNS_USE_GLOBAL_CACHE',
579: 'CURLOPT_FAILONERROR',
580: 'CURLOPT_SSL_FALSESTART',
581: 'CURLOPT_FILETIME',
582: 'CURLOPT_FOLLOWLOCATION',
583: 'CURLOPT_FORBID_REUSE',
584: 'CURLOPT_FRESH_CONNECT',
585: 'CURLOPT_FTP_USE_EPRT',
586: 'CURLOPT_FTP_USE_EPSV',
587: 'CURLOPT_FTP_CREATE_MISSING_DIRS',
588: 'CURLOPT_FTPAPPEND',
589: 'CURLOPT_TCP_NODELAY',
590: 'CURLOPT_FTPASCII',
591: 'CURLOPT_FTPLISTONLY',
592: 'CURLOPT_HEADER',
593: 'CURLOPT_HTTP09_ALLOWED',
594: 'CURLOPT_HTTPGET',
595: 'CURLOPT_HTTPPROXYTUNNEL',
596: 'CURLOPT_HTTP_CONTENT_DECODING',
597: 'CURLOPT_KEEP_SENDING_ON_ERROR',
598: 'CURLOPT_MUTE',
599: 'CURLOPT_NETRC',
600: 'CURLOPT_NOBODY',
601: 'CURLOPT_NOPROGRESS',
602: 'CURLOPT_NOSIGNAL',
603: 'CURLOPT_PATH_AS_IS',
604: 'CURLOPT_PIPEWAIT',
605: 'CURLOPT_POST',
606: 'CURLOPT_PUT',
607: 'CURLOPT_RETURNTRANSFER',
608: 'CURLOPT_SASL_IR',
609: 'CURLOPT_SSL_ENABLE_ALPN',
610: 'CURLOPT_SSL_ENABLE_NPN',
611: 'CURLOPT_SSL_VERIFYPEER',
612: 'CURLOPT_SSL_VERIFYSTATUS',
613: 'CURLOPT_PROXY_SSL_VERIFYPEER',
614: 'CURLOPT_SUPPRESS_CONNECT_HEADERS',
615: 'CURLOPT_TCP_FASTOPEN',
616: 'CURLOPT_TFTP_NO_OPTIONS',
617: 'CURLOPT_TRANSFERTEXT',
618: 'CURLOPT_UNRESTRICTED_AUTH',
619: 'CURLOPT_UPLOAD',
620: 'CURLOPT_VERBOSE',
621: ];
622: foreach ($boolConstants as $constName) {
623: if (defined($constName) && constant($constName) === $curlOpt) {
624: return new BooleanType();
625: }
626: }
627:
628: $intConstants = [
629: 'CURLOPT_BUFFERSIZE',
630: 'CURLOPT_CONNECTTIMEOUT',
631: 'CURLOPT_CONNECTTIMEOUT_MS',
632: 'CURLOPT_DNS_CACHE_TIMEOUT',
633: 'CURLOPT_EXPECT_100_TIMEOUT_MS',
634: 'CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS',
635: 'CURLOPT_FTPSSLAUTH',
636: 'CURLOPT_HEADEROPT',
637: 'CURLOPT_HTTP_VERSION',
638: 'CURLOPT_HTTPAUTH',
639: 'CURLOPT_INFILESIZE',
640: 'CURLOPT_LOW_SPEED_LIMIT',
641: 'CURLOPT_LOW_SPEED_TIME',
642: 'CURLOPT_MAXCONNECTS',
643: 'CURLOPT_MAXREDIRS',
644: 'CURLOPT_PORT',
645: 'CURLOPT_POSTREDIR',
646: 'CURLOPT_PROTOCOLS',
647: 'CURLOPT_PROXYAUTH',
648: 'CURLOPT_PROXYPORT',
649: 'CURLOPT_PROXYTYPE',
650: 'CURLOPT_REDIR_PROTOCOLS',
651: 'CURLOPT_RESUME_FROM',
652: 'CURLOPT_SOCKS5_AUTH',
653: 'CURLOPT_SSL_OPTIONS',
654: 'CURLOPT_SSL_VERIFYHOST',
655: 'CURLOPT_SSLVERSION',
656: 'CURLOPT_PROXY_SSL_OPTIONS',
657: 'CURLOPT_PROXY_SSL_VERIFYHOST',
658: 'CURLOPT_PROXY_SSLVERSION',
659: 'CURLOPT_STREAM_WEIGHT',
660: 'CURLOPT_TCP_KEEPALIVE',
661: 'CURLOPT_TCP_KEEPIDLE',
662: 'CURLOPT_TCP_KEEPINTVL',
663: 'CURLOPT_TIMECONDITION',
664: 'CURLOPT_TIMEOUT',
665: 'CURLOPT_TIMEOUT_MS',
666: 'CURLOPT_TIMEVALUE',
667: 'CURLOPT_TIMEVALUE_LARGE',
668: 'CURLOPT_MAX_RECV_SPEED_LARGE',
669: 'CURLOPT_SSH_AUTH_TYPES',
670: 'CURLOPT_IPRESOLVE',
671: 'CURLOPT_FTP_FILEMETHOD',
672: ];
673: foreach ($intConstants as $constName) {
674: if (defined($constName) && constant($constName) === $curlOpt) {
675: return new IntegerType();
676: }
677: }
678:
679: $nonEmptyStringConstants = [
680: 'CURLOPT_ABSTRACT_UNIX_SOCKET',
681: 'CURLOPT_CAINFO',
682: 'CURLOPT_CAPATH',
683: 'CURLOPT_COOKIE',
684: 'CURLOPT_COOKIEJAR',
685: 'CURLOPT_COOKIELIST',
686: 'CURLOPT_CUSTOMREQUEST',
687: 'CURLOPT_DEFAULT_PROTOCOL',
688: 'CURLOPT_DNS_INTERFACE',
689: 'CURLOPT_DNS_LOCAL_IP4',
690: 'CURLOPT_DNS_LOCAL_IP6',
691: 'CURLOPT_EGDSOCKET',
692: 'CURLOPT_FTPPORT',
693: 'CURLOPT_INTERFACE',
694: 'CURLOPT_KEYPASSWD',
695: 'CURLOPT_KRB4LEVEL',
696: 'CURLOPT_LOGIN_OPTIONS',
697: 'CURLOPT_PINNEDPUBLICKEY',
698: 'CURLOPT_PROXY_SERVICE_NAME',
699: 'CURLOPT_PROXY_CAINFO',
700: 'CURLOPT_PROXY_CAPATH',
701: 'CURLOPT_PROXY_CRLFILE',
702: 'CURLOPT_PROXY_KEYPASSWD',
703: 'CURLOPT_PROXY_PINNEDPUBLICKEY',
704: 'CURLOPT_PROXY_SSLCERT',
705: 'CURLOPT_PROXY_SSLCERTTYPE',
706: 'CURLOPT_PROXY_SSL_CIPHER_LIST',
707: 'CURLOPT_PROXY_TLS13_CIPHERS',
708: 'CURLOPT_PROXY_SSLKEY',
709: 'CURLOPT_PROXY_SSLKEYTYPE',
710: 'CURLOPT_PROXY_TLSAUTH_PASSWORD',
711: 'CURLOPT_PROXY_TLSAUTH_TYPE',
712: 'CURLOPT_PROXY_TLSAUTH_USERNAME',
713: 'CURLOPT_PROXYUSERPWD',
714: 'CURLOPT_RANDOM_FILE',
715: 'CURLOPT_RANGE',
716: 'CURLOPT_REFERER',
717: 'CURLOPT_SERVICE_NAME',
718: 'CURLOPT_SSH_HOST_PUBLIC_KEY_MD5',
719: 'CURLOPT_SSH_PUBLIC_KEYFILE',
720: 'CURLOPT_SSH_PRIVATE_KEYFILE',
721: 'CURLOPT_SSL_CIPHER_LIST',
722: 'CURLOPT_SSLCERT',
723: 'CURLOPT_SSLCERTPASSWD',
724: 'CURLOPT_SSLCERTTYPE',
725: 'CURLOPT_SSLENGINE',
726: 'CURLOPT_SSLENGINE_DEFAULT',
727: 'CURLOPT_SSLKEY',
728: 'CURLOPT_SSLKEYPASSWD',
729: 'CURLOPT_SSLKEYTYPE',
730: 'CURLOPT_TLS13_CIPHERS',
731: 'CURLOPT_UNIX_SOCKET_PATH',
732: 'CURLOPT_URL',
733: 'CURLOPT_USERAGENT',
734: 'CURLOPT_USERNAME',
735: 'CURLOPT_PASSWORD',
736: 'CURLOPT_USERPWD',
737: 'CURLOPT_XOAUTH2_BEARER',
738: ];
739: foreach ($nonEmptyStringConstants as $constName) {
740: if (defined($constName) && constant($constName) === $curlOpt) {
741: return TypeCombinator::intersect(
742: new StringType(),
743: new AccessoryNonEmptyStringType(),
744: );
745: }
746: }
747:
748: $stringConstants = [
749: 'CURLOPT_COOKIEFILE',
750: 'CURLOPT_ENCODING',
751: 'CURLOPT_PRE_PROXY',
752: 'CURLOPT_PRIVATE',
753: 'CURLOPT_PROXY',
754: ];
755: foreach ($stringConstants as $constName) {
756: if (defined($constName) && constant($constName) === $curlOpt) {
757: return new StringType();
758: }
759: }
760:
761: $intArrayStringKeysConstants = [
762: 'CURLOPT_HTTPHEADER',
763: ];
764: foreach ($intArrayStringKeysConstants as $constName) {
765: if (defined($constName) && constant($constName) === $curlOpt) {
766: return new ArrayType(new IntegerType(), new StringType());
767: }
768: }
769:
770: $arrayConstants = [
771: 'CURLOPT_CONNECT_TO',
772: 'CURLOPT_HTTP200ALIASES',
773: 'CURLOPT_POSTQUOTE',
774: 'CURLOPT_PROXYHEADER',
775: 'CURLOPT_QUOTE',
776: 'CURLOPT_RESOLVE',
777: ];
778: foreach ($arrayConstants as $constName) {
779: if (defined($constName) && constant($constName) === $curlOpt) {
780: return new ArrayType(new MixedType(), new MixedType());
781: }
782: }
783:
784: $arrayOrStringConstants = [
785: 'CURLOPT_POSTFIELDS',
786: ];
787: foreach ($arrayOrStringConstants as $constName) {
788: if (defined($constName) && constant($constName) === $curlOpt) {
789: return new UnionType([
790: new StringType(),
791: new ArrayType(new MixedType(), new MixedType()),
792: ]);
793: }
794: }
795:
796: $resourceConstants = [
797: 'CURLOPT_FILE',
798: 'CURLOPT_INFILE',
799: 'CURLOPT_STDERR',
800: 'CURLOPT_WRITEHEADER',
801: ];
802: foreach ($resourceConstants as $constName) {
803: if (defined($constName) && constant($constName) === $curlOpt) {
804: return new ResourceType();
805: }
806: }
807:
808: // unknown constant
809: return null;
810: }
811:
812: }
813: