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