1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type;
4:
5: use Closure;
6: use PHPStan\Analyser\OutOfClassScope;
7: use PHPStan\Node\InvalidateExprNode;
8: use PHPStan\Php\PhpVersion;
9: use PHPStan\PhpDoc\Tag\TemplateTag;
10: use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
11: use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
12: use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode;
13: use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
14: use PHPStan\PhpDocParser\Ast\Type\TypeNode;
15: use PHPStan\PhpDocParser\Printer\Printer;
16: use PHPStan\Reflection\Callables\CallableParametersAcceptor;
17: use PHPStan\Reflection\Callables\SimpleImpurePoint;
18: use PHPStan\Reflection\Callables\SimpleThrowPoint;
19: use PHPStan\Reflection\ClassConstantReflection;
20: use PHPStan\Reflection\ClassMemberAccessAnswerer;
21: use PHPStan\Reflection\ClassReflection;
22: use PHPStan\Reflection\ExtendedMethodReflection;
23: use PHPStan\Reflection\ExtendedPropertyReflection;
24: use PHPStan\Reflection\Native\NativeParameterReflection;
25: use PHPStan\Reflection\ParameterReflection;
26: use PHPStan\Reflection\ParametersAcceptor;
27: use PHPStan\Reflection\ParametersAcceptorSelector;
28: use PHPStan\Reflection\PassedByReference;
29: use PHPStan\Reflection\Php\ClosureCallUnresolvedMethodPrototypeReflection;
30: use PHPStan\Reflection\Php\DummyParameter;
31: use PHPStan\Reflection\Type\UnresolvedMethodPrototypeReflection;
32: use PHPStan\Reflection\Type\UnresolvedPropertyPrototypeReflection;
33: use PHPStan\TrinaryLogic;
34: use PHPStan\Type\Constant\ConstantArrayType;
35: use PHPStan\Type\Constant\ConstantBooleanType;
36: use PHPStan\Type\Constant\ConstantIntegerType;
37: use PHPStan\Type\Generic\TemplateType;
38: use PHPStan\Type\Generic\TemplateTypeHelper;
39: use PHPStan\Type\Generic\TemplateTypeMap;
40: use PHPStan\Type\Generic\TemplateTypeVariance;
41: use PHPStan\Type\Generic\TemplateTypeVarianceMap;
42: use PHPStan\Type\Traits\NonArrayTypeTrait;
43: use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
44: use PHPStan\Type\Traits\NonIterableTypeTrait;
45: use PHPStan\Type\Traits\NonOffsetAccessibleTypeTrait;
46: use PHPStan\Type\Traits\NonRemoveableTypeTrait;
47: use PHPStan\Type\Traits\UndecidedComparisonTypeTrait;
48: use function array_map;
49: use function array_merge;
50: use function count;
51:
52: /** @api */
53: class ClosureType implements TypeWithClassName, CallableParametersAcceptor
54: {
55:
56: use NonArrayTypeTrait;
57: use NonIterableTypeTrait;
58: use UndecidedComparisonTypeTrait;
59: use NonOffsetAccessibleTypeTrait;
60: use NonRemoveableTypeTrait;
61: use NonGeneralizableTypeTrait;
62:
63: /** @var list<ParameterReflection> */
64: private array $parameters;
65:
66: private Type $returnType;
67:
68: private bool $isCommonCallable;
69:
70: private ObjectType $objectType;
71:
72: private TemplateTypeMap $templateTypeMap;
73:
74: private TemplateTypeMap $resolvedTemplateTypeMap;
75:
76: private TemplateTypeVarianceMap $callSiteVarianceMap;
77:
78: /** @var SimpleImpurePoint[] */
79: private array $impurePoints;
80:
81: private TrinaryLogic $acceptsNamedArguments;
82:
83: /**
84: * @api
85: * @param list<ParameterReflection>|null $parameters
86: * @param array<non-empty-string, TemplateTag> $templateTags
87: * @param SimpleThrowPoint[] $throwPoints
88: * @param ?SimpleImpurePoint[] $impurePoints
89: * @param InvalidateExprNode[] $invalidateExpressions
90: * @param string[] $usedVariables
91: */
92: public function __construct(
93: ?array $parameters = null,
94: ?Type $returnType = null,
95: private bool $variadic = true,
96: ?TemplateTypeMap $templateTypeMap = null,
97: ?TemplateTypeMap $resolvedTemplateTypeMap = null,
98: ?TemplateTypeVarianceMap $callSiteVarianceMap = null,
99: private array $templateTags = [],
100: private array $throwPoints = [],
101: ?array $impurePoints = null,
102: private array $invalidateExpressions = [],
103: private array $usedVariables = [],
104: ?TrinaryLogic $acceptsNamedArguments = null,
105: )
106: {
107: if ($acceptsNamedArguments === null) {
108: $acceptsNamedArguments = TrinaryLogic::createYes();
109: }
110: $this->acceptsNamedArguments = $acceptsNamedArguments;
111:
112: $this->parameters = $parameters ?? [];
113: $this->returnType = $returnType ?? new MixedType();
114: $this->isCommonCallable = $parameters === null && $returnType === null;
115: $this->objectType = new ObjectType(Closure::class);
116: $this->templateTypeMap = $templateTypeMap ?? TemplateTypeMap::createEmpty();
117: $this->resolvedTemplateTypeMap = $resolvedTemplateTypeMap ?? TemplateTypeMap::createEmpty();
118: $this->callSiteVarianceMap = $callSiteVarianceMap ?? TemplateTypeVarianceMap::createEmpty();
119: $this->impurePoints = $impurePoints ?? [new SimpleImpurePoint('functionCall', 'call to an unknown Closure', false)];
120: }
121:
122: /**
123: * @return array<non-empty-string, TemplateTag>
124: */
125: public function getTemplateTags(): array
126: {
127: return $this->templateTags;
128: }
129:
130: public static function createPure(): self
131: {
132: return new self(null, null, true, null, null, null, [], [], []);
133: }
134:
135: public function isPure(): TrinaryLogic
136: {
137: $impurePoints = $this->getImpurePoints();
138: if (count($impurePoints) === 0) {
139: return TrinaryLogic::createYes();
140: }
141:
142: $certainCount = 0;
143: foreach ($impurePoints as $impurePoint) {
144: if (!$impurePoint->isCertain()) {
145: continue;
146: }
147:
148: $certainCount++;
149: }
150:
151: return $certainCount > 0 ? TrinaryLogic::createNo() : TrinaryLogic::createMaybe();
152: }
153:
154: public function getClassName(): string
155: {
156: return $this->objectType->getClassName();
157: }
158:
159: public function getClassReflection(): ?ClassReflection
160: {
161: return $this->objectType->getClassReflection();
162: }
163:
164: public function getAncestorWithClassName(string $className): ?TypeWithClassName
165: {
166: return $this->objectType->getAncestorWithClassName($className);
167: }
168:
169: public function getReferencedClasses(): array
170: {
171: $classes = $this->objectType->getReferencedClasses();
172: foreach ($this->parameters as $parameter) {
173: $classes = array_merge($classes, $parameter->getType()->getReferencedClasses());
174: }
175:
176: return array_merge($classes, $this->returnType->getReferencedClasses());
177: }
178:
179: public function getObjectClassNames(): array
180: {
181: return $this->objectType->getObjectClassNames();
182: }
183:
184: public function getObjectClassReflections(): array
185: {
186: return $this->objectType->getObjectClassReflections();
187: }
188:
189: public function accepts(Type $type, bool $strictTypes): AcceptsResult
190: {
191: if ($type instanceof CompoundType) {
192: return $type->isAcceptedBy($this, $strictTypes);
193: }
194:
195: if (!$type instanceof ClosureType) {
196: return $this->objectType->accepts($type, $strictTypes);
197: }
198:
199: return $this->isSuperTypeOfInternal($type, true)->toAcceptsResult();
200: }
201:
202: public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
203: {
204: if ($type instanceof CompoundType) {
205: return $type->isSubTypeOf($this);
206: }
207:
208: return $this->isSuperTypeOfInternal($type, false);
209: }
210:
211: private function isSuperTypeOfInternal(Type $type, bool $treatMixedAsAny): IsSuperTypeOfResult
212: {
213: if ($type instanceof self) {
214: $parameterTypes = array_map(static fn ($parameter) => $parameter->getType(), $this->getParameters());
215: $variant = ParametersAcceptorSelector::selectFromTypes($parameterTypes, [$type], false);
216: if (!$variant instanceof CallableParametersAcceptor) {
217: return IsSuperTypeOfResult::createNo([]);
218: }
219: return CallableTypeHelper::isParametersAcceptorSuperTypeOf(
220: $this,
221: $variant,
222: $treatMixedAsAny,
223: );
224: }
225:
226: if ($type->getObjectClassNames() === [Closure::class]) {
227: return IsSuperTypeOfResult::createMaybe();
228: }
229:
230: return $this->objectType->isSuperTypeOf($type);
231: }
232:
233: public function equals(Type $type): bool
234: {
235: if (!$type instanceof self) {
236: return false;
237: }
238:
239: return $this->describe(VerbosityLevel::precise()) === $type->describe(VerbosityLevel::precise())
240: && $this->isPure()->equals($type->isPure());
241: }
242:
243: public function describe(VerbosityLevel $level): string
244: {
245: return $level->handle(
246: static fn (): string => 'Closure',
247: function (): string {
248: if ($this->isCommonCallable) {
249: return $this->isPure()->yes() ? 'pure-Closure' : 'Closure';
250: }
251:
252: $printer = new Printer();
253: $selfWithoutParameterNames = new self(
254: array_map(static fn (ParameterReflection $p): ParameterReflection => new DummyParameter(
255: '',
256: $p->getType(),
257: $p->isOptional() && !$p->isVariadic(),
258: PassedByReference::createNo(),
259: $p->isVariadic(),
260: $p->getDefaultValue(),
261: ), $this->parameters),
262: $this->returnType,
263: $this->variadic,
264: $this->templateTypeMap,
265: $this->resolvedTemplateTypeMap,
266: $this->callSiteVarianceMap,
267: $this->templateTags,
268: $this->throwPoints,
269: $this->impurePoints,
270: $this->invalidateExpressions,
271: $this->usedVariables,
272: );
273:
274: return $printer->print($selfWithoutParameterNames->toPhpDocNode());
275: },
276: );
277: }
278:
279: public function isOffsetAccessLegal(): TrinaryLogic
280: {
281: return TrinaryLogic::createNo();
282: }
283:
284: public function isObject(): TrinaryLogic
285: {
286: return $this->objectType->isObject();
287: }
288:
289: public function isEnum(): TrinaryLogic
290: {
291: return $this->objectType->isEnum();
292: }
293:
294: public function getTemplateType(string $ancestorClassName, string $templateTypeName): Type
295: {
296: return $this->objectType->getTemplateType($ancestorClassName, $templateTypeName);
297: }
298:
299: public function canAccessProperties(): TrinaryLogic
300: {
301: return $this->objectType->canAccessProperties();
302: }
303:
304: public function hasProperty(string $propertyName): TrinaryLogic
305: {
306: return $this->objectType->hasProperty($propertyName);
307: }
308:
309: public function getProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
310: {
311: return $this->objectType->getProperty($propertyName, $scope);
312: }
313:
314: public function getUnresolvedPropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection
315: {
316: return $this->objectType->getUnresolvedPropertyPrototype($propertyName, $scope);
317: }
318:
319: public function hasInstanceProperty(string $propertyName): TrinaryLogic
320: {
321: return $this->objectType->hasInstanceProperty($propertyName);
322: }
323:
324: public function getInstanceProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
325: {
326: return $this->objectType->getInstanceProperty($propertyName, $scope);
327: }
328:
329: public function getUnresolvedInstancePropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection
330: {
331: return $this->objectType->getUnresolvedInstancePropertyPrototype($propertyName, $scope);
332: }
333:
334: public function hasStaticProperty(string $propertyName): TrinaryLogic
335: {
336: return $this->objectType->hasStaticProperty($propertyName);
337: }
338:
339: public function getStaticProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
340: {
341: return $this->objectType->getStaticProperty($propertyName, $scope);
342: }
343:
344: public function getUnresolvedStaticPropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection
345: {
346: return $this->objectType->getUnresolvedStaticPropertyPrototype($propertyName, $scope);
347: }
348:
349: public function canCallMethods(): TrinaryLogic
350: {
351: return $this->objectType->canCallMethods();
352: }
353:
354: public function hasMethod(string $methodName): TrinaryLogic
355: {
356: return $this->objectType->hasMethod($methodName);
357: }
358:
359: public function getMethod(string $methodName, ClassMemberAccessAnswerer $scope): ExtendedMethodReflection
360: {
361: return $this->getUnresolvedMethodPrototype($methodName, $scope)->getTransformedMethod();
362: }
363:
364: public function getUnresolvedMethodPrototype(string $methodName, ClassMemberAccessAnswerer $scope): UnresolvedMethodPrototypeReflection
365: {
366: if ($methodName === 'call') {
367: return new ClosureCallUnresolvedMethodPrototypeReflection(
368: $this->objectType->getUnresolvedMethodPrototype($methodName, $scope),
369: $this,
370: );
371: }
372:
373: return $this->objectType->getUnresolvedMethodPrototype($methodName, $scope);
374: }
375:
376: public function canAccessConstants(): TrinaryLogic
377: {
378: return $this->objectType->canAccessConstants();
379: }
380:
381: public function hasConstant(string $constantName): TrinaryLogic
382: {
383: return $this->objectType->hasConstant($constantName);
384: }
385:
386: public function getConstant(string $constantName): ClassConstantReflection
387: {
388: return $this->objectType->getConstant($constantName);
389: }
390:
391: public function getConstantStrings(): array
392: {
393: return [];
394: }
395:
396: public function isIterable(): TrinaryLogic
397: {
398: return TrinaryLogic::createNo();
399: }
400:
401: public function isIterableAtLeastOnce(): TrinaryLogic
402: {
403: return TrinaryLogic::createNo();
404: }
405:
406: public function isCallable(): TrinaryLogic
407: {
408: return TrinaryLogic::createYes();
409: }
410:
411: public function getEnumCases(): array
412: {
413: return [];
414: }
415:
416: public function isCommonCallable(): bool
417: {
418: return $this->isCommonCallable;
419: }
420:
421: public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope): array
422: {
423: return [$this];
424: }
425:
426: public function getThrowPoints(): array
427: {
428: return $this->throwPoints;
429: }
430:
431: public function getImpurePoints(): array
432: {
433: return $this->impurePoints;
434: }
435:
436: public function getInvalidateExpressions(): array
437: {
438: return $this->invalidateExpressions;
439: }
440:
441: public function getUsedVariables(): array
442: {
443: return $this->usedVariables;
444: }
445:
446: public function acceptsNamedArguments(): TrinaryLogic
447: {
448: return $this->acceptsNamedArguments;
449: }
450:
451: public function isCloneable(): TrinaryLogic
452: {
453: return TrinaryLogic::createYes();
454: }
455:
456: public function toBoolean(): BooleanType
457: {
458: return new ConstantBooleanType(true);
459: }
460:
461: public function toNumber(): Type
462: {
463: return new ErrorType();
464: }
465:
466: public function toAbsoluteNumber(): Type
467: {
468: return new ErrorType();
469: }
470:
471: public function toInteger(): Type
472: {
473: return new ErrorType();
474: }
475:
476: public function toFloat(): Type
477: {
478: return new ErrorType();
479: }
480:
481: public function toString(): Type
482: {
483: return new ErrorType();
484: }
485:
486: public function toArray(): Type
487: {
488: return new ConstantArrayType(
489: [new ConstantIntegerType(0)],
490: [$this],
491: [1],
492: isList: TrinaryLogic::createYes(),
493: );
494: }
495:
496: public function toArrayKey(): Type
497: {
498: return new ErrorType();
499: }
500:
501: public function toCoercedArgumentType(bool $strictTypes): Type
502: {
503: return TypeCombinator::union($this, new CallableType());
504: }
505:
506: public function getTemplateTypeMap(): TemplateTypeMap
507: {
508: return $this->templateTypeMap;
509: }
510:
511: public function getResolvedTemplateTypeMap(): TemplateTypeMap
512: {
513: return $this->resolvedTemplateTypeMap;
514: }
515:
516: public function getCallSiteVarianceMap(): TemplateTypeVarianceMap
517: {
518: return $this->callSiteVarianceMap;
519: }
520:
521: /**
522: * @return list<ParameterReflection>
523: */
524: public function getParameters(): array
525: {
526: return $this->parameters;
527: }
528:
529: public function isVariadic(): bool
530: {
531: return $this->variadic;
532: }
533:
534: public function getReturnType(): Type
535: {
536: return $this->returnType;
537: }
538:
539: public function inferTemplateTypes(Type $receivedType): TemplateTypeMap
540: {
541: if ($receivedType instanceof UnionType || $receivedType instanceof IntersectionType) {
542: return $receivedType->inferTemplateTypesOn($this);
543: }
544:
545: if ($receivedType->isCallable()->no() || ! $receivedType instanceof self) {
546: return TemplateTypeMap::createEmpty();
547: }
548:
549: $parametersAcceptors = $receivedType->getCallableParametersAcceptors(new OutOfClassScope());
550:
551: $typeMap = TemplateTypeMap::createEmpty();
552:
553: foreach ($parametersAcceptors as $parametersAcceptor) {
554: $typeMap = $typeMap->union($this->inferTemplateTypesOnParametersAcceptor($parametersAcceptor));
555: }
556:
557: return $typeMap;
558: }
559:
560: private function inferTemplateTypesOnParametersAcceptor(ParametersAcceptor $parametersAcceptor): TemplateTypeMap
561: {
562: $parameterTypes = array_map(static fn ($parameter) => $parameter->getType(), $this->getParameters());
563: $parametersAcceptor = ParametersAcceptorSelector::selectFromTypes($parameterTypes, [$parametersAcceptor], false);
564: $args = $parametersAcceptor->getParameters();
565: $returnType = $parametersAcceptor->getReturnType();
566:
567: $typeMap = TemplateTypeMap::createEmpty();
568: foreach ($this->getParameters() as $i => $param) {
569: $paramType = $param->getType();
570: if (isset($args[$i])) {
571: $argType = $args[$i]->getType();
572: } elseif ($paramType instanceof TemplateType) {
573: $argType = TemplateTypeHelper::resolveToBounds($paramType);
574: } else {
575: $argType = new NeverType();
576: }
577:
578: $typeMap = $typeMap->union($paramType->inferTemplateTypes($argType)->convertToLowerBoundTypes());
579: }
580:
581: return $typeMap->union($this->getReturnType()->inferTemplateTypes($returnType));
582: }
583:
584: public function getReferencedTemplateTypes(TemplateTypeVariance $positionVariance): array
585: {
586: $references = $this->getReturnType()->getReferencedTemplateTypes(
587: $positionVariance->compose(TemplateTypeVariance::createCovariant()),
588: );
589:
590: $paramVariance = $positionVariance->compose(TemplateTypeVariance::createContravariant());
591:
592: foreach ($this->getParameters() as $param) {
593: foreach ($param->getType()->getReferencedTemplateTypes($paramVariance) as $reference) {
594: $references[] = $reference;
595: }
596: }
597:
598: return $references;
599: }
600:
601: public function traverse(callable $cb): Type
602: {
603: if ($this->isCommonCallable) {
604: return $this;
605: }
606:
607: return new self(
608: array_map(static function (ParameterReflection $param) use ($cb): NativeParameterReflection {
609: $defaultValue = $param->getDefaultValue();
610: return new NativeParameterReflection(
611: $param->getName(),
612: $param->isOptional(),
613: $cb($param->getType()),
614: $param->passedByReference(),
615: $param->isVariadic(),
616: $defaultValue !== null ? $cb($defaultValue) : null,
617: );
618: }, $this->getParameters()),
619: $cb($this->getReturnType()),
620: $this->isVariadic(),
621: $this->templateTypeMap,
622: $this->resolvedTemplateTypeMap,
623: $this->callSiteVarianceMap,
624: $this->templateTags,
625: $this->throwPoints,
626: $this->impurePoints,
627: $this->invalidateExpressions,
628: $this->usedVariables,
629: $this->acceptsNamedArguments,
630: );
631: }
632:
633: public function traverseSimultaneously(Type $right, callable $cb): Type
634: {
635: if ($this->isCommonCallable) {
636: return $this;
637: }
638:
639: if (!$right instanceof self) {
640: return $this;
641: }
642:
643: $rightParameters = $right->getParameters();
644: if (count($this->getParameters()) !== count($rightParameters)) {
645: return $this;
646: }
647:
648: $parameters = [];
649: foreach ($this->getParameters() as $i => $leftParam) {
650: $rightParam = $rightParameters[$i];
651: $leftDefaultValue = $leftParam->getDefaultValue();
652: $rightDefaultValue = $rightParam->getDefaultValue();
653: $defaultValue = $leftDefaultValue;
654: if ($leftDefaultValue !== null && $rightDefaultValue !== null) {
655: $defaultValue = $cb($leftDefaultValue, $rightDefaultValue);
656: }
657: $parameters[] = new NativeParameterReflection(
658: $leftParam->getName(),
659: $leftParam->isOptional(),
660: $cb($leftParam->getType(), $rightParam->getType()),
661: $leftParam->passedByReference(),
662: $leftParam->isVariadic(),
663: $defaultValue,
664: );
665: }
666:
667: return new self(
668: $parameters,
669: $cb($this->getReturnType(), $right->getReturnType()),
670: $this->isVariadic(),
671: $this->templateTypeMap,
672: $this->resolvedTemplateTypeMap,
673: $this->callSiteVarianceMap,
674: $this->templateTags,
675: $this->throwPoints,
676: $this->impurePoints,
677: $this->invalidateExpressions,
678: $this->usedVariables,
679: $this->acceptsNamedArguments,
680: );
681: }
682:
683: public function isNull(): TrinaryLogic
684: {
685: return TrinaryLogic::createNo();
686: }
687:
688: public function isConstantValue(): TrinaryLogic
689: {
690: return TrinaryLogic::createNo();
691: }
692:
693: public function isConstantScalarValue(): TrinaryLogic
694: {
695: return TrinaryLogic::createNo();
696: }
697:
698: public function getConstantScalarTypes(): array
699: {
700: return [];
701: }
702:
703: public function getConstantScalarValues(): array
704: {
705: return [];
706: }
707:
708: public function isTrue(): TrinaryLogic
709: {
710: return TrinaryLogic::createNo();
711: }
712:
713: public function isFalse(): TrinaryLogic
714: {
715: return TrinaryLogic::createNo();
716: }
717:
718: public function isBoolean(): TrinaryLogic
719: {
720: return TrinaryLogic::createNo();
721: }
722:
723: public function isFloat(): TrinaryLogic
724: {
725: return TrinaryLogic::createNo();
726: }
727:
728: public function isInteger(): TrinaryLogic
729: {
730: return TrinaryLogic::createNo();
731: }
732:
733: public function isString(): TrinaryLogic
734: {
735: return TrinaryLogic::createNo();
736: }
737:
738: public function isNumericString(): TrinaryLogic
739: {
740: return TrinaryLogic::createNo();
741: }
742:
743: public function isNonEmptyString(): TrinaryLogic
744: {
745: return TrinaryLogic::createNo();
746: }
747:
748: public function isNonFalsyString(): TrinaryLogic
749: {
750: return TrinaryLogic::createNo();
751: }
752:
753: public function isLiteralString(): TrinaryLogic
754: {
755: return TrinaryLogic::createNo();
756: }
757:
758: public function isLowercaseString(): TrinaryLogic
759: {
760: return TrinaryLogic::createNo();
761: }
762:
763: public function isClassString(): TrinaryLogic
764: {
765: return TrinaryLogic::createNo();
766: }
767:
768: public function isUppercaseString(): TrinaryLogic
769: {
770: return TrinaryLogic::createNo();
771: }
772:
773: public function getClassStringObjectType(): Type
774: {
775: return new ErrorType();
776: }
777:
778: public function getObjectTypeOrClassStringObjectType(): Type
779: {
780: return $this;
781: }
782:
783: public function isVoid(): TrinaryLogic
784: {
785: return TrinaryLogic::createNo();
786: }
787:
788: public function isScalar(): TrinaryLogic
789: {
790: return TrinaryLogic::createNo();
791: }
792:
793: public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
794: {
795: return new BooleanType();
796: }
797:
798: public function exponentiate(Type $exponent): Type
799: {
800: return new ErrorType();
801: }
802:
803: public function getFiniteTypes(): array
804: {
805: return [];
806: }
807:
808: public function toPhpDocNode(): TypeNode
809: {
810: if ($this->isCommonCallable) {
811: return new IdentifierTypeNode($this->isPure()->yes() ? 'pure-Closure' : 'Closure');
812: }
813:
814: $parameters = [];
815: foreach ($this->parameters as $parameter) {
816: $parameters[] = new CallableTypeParameterNode(
817: $parameter->getType()->toPhpDocNode(),
818: !$parameter->passedByReference()->no(),
819: $parameter->isVariadic(),
820: $parameter->getName() === '' ? '' : '$' . $parameter->getName(),
821: $parameter->isOptional(),
822: );
823: }
824:
825: $templateTags = [];
826: foreach ($this->templateTags as $templateName => $templateTag) {
827: $templateTags[] = new TemplateTagValueNode(
828: $templateName,
829: $templateTag->getBound()->toPhpDocNode(),
830: '',
831: );
832: }
833:
834: return new CallableTypeNode(
835: new IdentifierTypeNode('Closure'),
836: $parameters,
837: $this->returnType->toPhpDocNode(),
838: $templateTags,
839: );
840: }
841:
842: }
843: