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