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