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