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