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