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