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)->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): 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: );
253: }
254:
255: if ($type->getObjectClassNames() === [Closure::class]) {
256: return IsSuperTypeOfResult::createMaybe();
257: }
258:
259: return $this->objectType->isSuperTypeOf($type);
260: }
261:
262: public function equals(Type $type): bool
263: {
264: if (!$type instanceof self) {
265: return false;
266: }
267:
268: return $this->describe(VerbosityLevel::precise()) === $type->describe(VerbosityLevel::precise())
269: && $this->isPure()->equals($type->isPure());
270: }
271:
272: public function describe(VerbosityLevel $level): string
273: {
274: return $level->handle(
275: static fn (): string => 'Closure',
276: function (): string {
277: if ($this->isCommonCallable) {
278: return $this->isPure()->yes() ? 'pure-Closure' : 'Closure';
279: }
280:
281: $printer = new Printer();
282: $selfWithoutParameterNames = new self(
283: array_map(static fn (ParameterReflection $p): ParameterReflection => new DummyParameter(
284: '',
285: $p->getType(),
286: optional: $p->isOptional() && !$p->isVariadic(),
287: passedByReference: PassedByReference::createNo(),
288: variadic: $p->isVariadic(),
289: defaultValue: $p->getDefaultValue(),
290: ), $this->parameters),
291: $this->returnType,
292: $this->variadic,
293: $this->templateTypeMap,
294: $this->resolvedTemplateTypeMap,
295: $this->callSiteVarianceMap,
296: $this->templateTags,
297: $this->throwPoints,
298: $this->impurePoints,
299: $this->invalidateExpressions,
300: $this->usedVariables,
301: $this->acceptsNamedArguments,
302: $this->mustUseReturnValue,
303: );
304:
305: return $printer->print($selfWithoutParameterNames->toPhpDocNode());
306: },
307: );
308: }
309:
310: public function isOffsetAccessLegal(): TrinaryLogic
311: {
312: return TrinaryLogic::createNo();
313: }
314:
315: public function isObject(): TrinaryLogic
316: {
317: return $this->objectType->isObject();
318: }
319:
320: public function getClassStringType(): Type
321: {
322: return $this->objectType->getClassStringType();
323: }
324:
325: public function isEnum(): TrinaryLogic
326: {
327: return $this->objectType->isEnum();
328: }
329:
330: public function getTemplateType(string $ancestorClassName, string $templateTypeName): Type
331: {
332: return $this->objectType->getTemplateType($ancestorClassName, $templateTypeName);
333: }
334:
335: public function canAccessProperties(): TrinaryLogic
336: {
337: return $this->objectType->canAccessProperties();
338: }
339:
340: public function hasProperty(string $propertyName): TrinaryLogic
341: {
342: return $this->objectType->hasProperty($propertyName);
343: }
344:
345: public function getProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
346: {
347: return $this->objectType->getProperty($propertyName, $scope);
348: }
349:
350: public function getUnresolvedPropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection
351: {
352: return $this->objectType->getUnresolvedPropertyPrototype($propertyName, $scope);
353: }
354:
355: public function hasInstanceProperty(string $propertyName): TrinaryLogic
356: {
357: return $this->objectType->hasInstanceProperty($propertyName);
358: }
359:
360: public function getInstanceProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
361: {
362: return $this->objectType->getInstanceProperty($propertyName, $scope);
363: }
364:
365: public function getUnresolvedInstancePropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection
366: {
367: return $this->objectType->getUnresolvedInstancePropertyPrototype($propertyName, $scope);
368: }
369:
370: public function hasStaticProperty(string $propertyName): TrinaryLogic
371: {
372: return $this->objectType->hasStaticProperty($propertyName);
373: }
374:
375: public function getStaticProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
376: {
377: return $this->objectType->getStaticProperty($propertyName, $scope);
378: }
379:
380: public function getUnresolvedStaticPropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection
381: {
382: return $this->objectType->getUnresolvedStaticPropertyPrototype($propertyName, $scope);
383: }
384:
385: public function canCallMethods(): TrinaryLogic
386: {
387: return $this->objectType->canCallMethods();
388: }
389:
390: public function hasMethod(string $methodName): TrinaryLogic
391: {
392: return $this->objectType->hasMethod($methodName);
393: }
394:
395: public function getMethod(string $methodName, ClassMemberAccessAnswerer $scope): ExtendedMethodReflection
396: {
397: return $this->getUnresolvedMethodPrototype($methodName, $scope)->getTransformedMethod();
398: }
399:
400: public function getUnresolvedMethodPrototype(string $methodName, ClassMemberAccessAnswerer $scope): UnresolvedMethodPrototypeReflection
401: {
402: if ($methodName === 'call') {
403: return new ClosureCallUnresolvedMethodPrototypeReflection(
404: $this->objectType->getUnresolvedMethodPrototype($methodName, $scope),
405: $this,
406: );
407: }
408:
409: return $this->objectType->getUnresolvedMethodPrototype($methodName, $scope);
410: }
411:
412: public function canAccessConstants(): TrinaryLogic
413: {
414: return $this->objectType->canAccessConstants();
415: }
416:
417: public function hasConstant(string $constantName): TrinaryLogic
418: {
419: return $this->objectType->hasConstant($constantName);
420: }
421:
422: public function getConstant(string $constantName): ClassConstantReflection
423: {
424: return $this->objectType->getConstant($constantName);
425: }
426:
427: public function getConstantStrings(): array
428: {
429: return [];
430: }
431:
432: public function isIterable(): TrinaryLogic
433: {
434: return TrinaryLogic::createNo();
435: }
436:
437: public function isIterableAtLeastOnce(): TrinaryLogic
438: {
439: return TrinaryLogic::createNo();
440: }
441:
442: public function isCallable(): TrinaryLogic
443: {
444: return TrinaryLogic::createYes();
445: }
446:
447: public function getEnumCases(): array
448: {
449: return [];
450: }
451:
452: public function getEnumCaseObject(): ?EnumCaseObjectType
453: {
454: return null;
455: }
456:
457: public function isCommonCallable(): bool
458: {
459: return $this->isCommonCallable;
460: }
461:
462: public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope): array
463: {
464: return [$this];
465: }
466:
467: public function getThrowPoints(): array
468: {
469: return $this->throwPoints;
470: }
471:
472: public function getImpurePoints(): array
473: {
474: return $this->impurePoints;
475: }
476:
477: public function getInvalidateExpressions(): array
478: {
479: return $this->invalidateExpressions;
480: }
481:
482: public function getUsedVariables(): array
483: {
484: return $this->usedVariables;
485: }
486:
487: public function acceptsNamedArguments(): TrinaryLogic
488: {
489: return $this->acceptsNamedArguments;
490: }
491:
492: public function mustUseReturnValue(): TrinaryLogic
493: {
494: return $this->mustUseReturnValue;
495: }
496:
497: public function isCloneable(): TrinaryLogic
498: {
499: return TrinaryLogic::createYes();
500: }
501:
502: public function toBoolean(): BooleanType
503: {
504: return new ConstantBooleanType(true);
505: }
506:
507: public function toNumber(): Type
508: {
509: return new ErrorType();
510: }
511:
512: public function toAbsoluteNumber(): Type
513: {
514: return new ErrorType();
515: }
516:
517: public function toInteger(): Type
518: {
519: return new ErrorType();
520: }
521:
522: public function toFloat(): Type
523: {
524: return new ErrorType();
525: }
526:
527: public function toString(): Type
528: {
529: return new ErrorType();
530: }
531:
532: public function toArray(): Type
533: {
534: return new ConstantArrayType(
535: [new ConstantIntegerType(0)],
536: [$this],
537: [1],
538: isList: TrinaryLogic::createYes(),
539: );
540: }
541:
542: public function toArrayKey(): Type
543: {
544: return new ErrorType();
545: }
546:
547: public function toCoercedArgumentType(bool $strictTypes): Type
548: {
549: return TypeCombinator::union($this, new CallableType());
550: }
551:
552: public function getTemplateTypeMap(): TemplateTypeMap
553: {
554: return $this->templateTypeMap;
555: }
556:
557: public function getResolvedTemplateTypeMap(): TemplateTypeMap
558: {
559: return $this->resolvedTemplateTypeMap;
560: }
561:
562: public function getCallSiteVarianceMap(): TemplateTypeVarianceMap
563: {
564: return $this->callSiteVarianceMap;
565: }
566:
567: /**
568: * @return list<ParameterReflection>
569: */
570: public function getParameters(): array
571: {
572: return $this->parameters;
573: }
574:
575: public function isVariadic(): bool
576: {
577: return $this->variadic;
578: }
579:
580: public function getReturnType(): Type
581: {
582: return $this->returnType;
583: }
584:
585: public function inferTemplateTypes(Type $receivedType): TemplateTypeMap
586: {
587: if ($receivedType instanceof UnionType || $receivedType instanceof IntersectionType) {
588: return $receivedType->inferTemplateTypesOn($this);
589: }
590:
591: if ($receivedType->isCallable()->no() || ! $receivedType instanceof self) {
592: return TemplateTypeMap::createEmpty();
593: }
594:
595: $parametersAcceptors = $receivedType->getCallableParametersAcceptors(new OutOfClassScope());
596:
597: $typeMap = TemplateTypeMap::createEmpty();
598:
599: foreach ($parametersAcceptors as $parametersAcceptor) {
600: $typeMap = $typeMap->union($this->inferTemplateTypesOnParametersAcceptor($parametersAcceptor));
601: }
602:
603: return $typeMap;
604: }
605:
606: private function inferTemplateTypesOnParametersAcceptor(ParametersAcceptor $parametersAcceptor): TemplateTypeMap
607: {
608: $parameterTypes = array_map(static fn ($parameter) => $parameter->getType(), $this->getParameters());
609: $parametersAcceptor = ParametersAcceptorSelector::selectFromTypes($parameterTypes, [$parametersAcceptor], false);
610: $args = $parametersAcceptor->getParameters();
611: $returnType = $parametersAcceptor->getReturnType();
612:
613: $typeMap = TemplateTypeMap::createEmpty();
614: foreach ($this->getParameters() as $i => $param) {
615: $paramType = $param->getType();
616: if (isset($args[$i])) {
617: $argType = $args[$i]->getType();
618: } elseif ($paramType instanceof TemplateType) {
619: $argType = TemplateTypeHelper::resolveToBounds($paramType);
620: } else {
621: $argType = new NeverType();
622: }
623:
624: $typeMap = $typeMap->union($paramType->inferTemplateTypes($argType)->convertToLowerBoundTypes());
625: }
626:
627: return $typeMap->union($this->getReturnType()->inferTemplateTypes($returnType));
628: }
629:
630: public function getReferencedTemplateTypes(TemplateTypeVariance $positionVariance): array
631: {
632: $references = $this->getReturnType()->getReferencedTemplateTypes(
633: $positionVariance->compose(TemplateTypeVariance::createCovariant()),
634: );
635:
636: $paramVariance = $positionVariance->compose(TemplateTypeVariance::createContravariant());
637:
638: foreach ($this->getParameters() as $param) {
639: foreach ($param->getType()->getReferencedTemplateTypes($paramVariance) as $reference) {
640: $references[] = $reference;
641: }
642: }
643:
644: return $references;
645: }
646:
647: public function traverse(callable $cb): Type
648: {
649: if ($this->isCommonCallable) {
650: return $this;
651: }
652:
653: return new self(
654: array_map(static function (ParameterReflection $param) use ($cb): NativeParameterReflection {
655: $defaultValue = $param->getDefaultValue();
656: return new NativeParameterReflection(
657: $param->getName(),
658: $param->isOptional(),
659: $cb($param->getType()),
660: $param->passedByReference(),
661: $param->isVariadic(),
662: $defaultValue !== null ? $cb($defaultValue) : null,
663: );
664: }, $this->getParameters()),
665: $cb($this->getReturnType()),
666: $this->isVariadic(),
667: $this->templateTypeMap,
668: $this->resolvedTemplateTypeMap,
669: $this->callSiteVarianceMap,
670: $this->templateTags,
671: $this->throwPoints,
672: $this->impurePoints,
673: $this->invalidateExpressions,
674: $this->usedVariables,
675: $this->acceptsNamedArguments,
676: $this->mustUseReturnValue,
677: $this->assertions,
678: );
679: }
680:
681: public function traverseSimultaneously(Type $right, callable $cb): Type
682: {
683: if ($this->isCommonCallable) {
684: return $this;
685: }
686:
687: if (!$right instanceof self) {
688: return $this;
689: }
690:
691: $rightParameters = $right->getParameters();
692: if (count($this->getParameters()) !== count($rightParameters)) {
693: return $this;
694: }
695:
696: $parameters = [];
697: foreach ($this->getParameters() as $i => $leftParam) {
698: $rightParam = $rightParameters[$i];
699: $leftDefaultValue = $leftParam->getDefaultValue();
700: $rightDefaultValue = $rightParam->getDefaultValue();
701: $defaultValue = $leftDefaultValue;
702: if ($leftDefaultValue !== null && $rightDefaultValue !== null) {
703: $defaultValue = $cb($leftDefaultValue, $rightDefaultValue);
704: }
705: $parameters[] = new NativeParameterReflection(
706: $leftParam->getName(),
707: $leftParam->isOptional(),
708: $cb($leftParam->getType(), $rightParam->getType()),
709: $leftParam->passedByReference(),
710: $leftParam->isVariadic(),
711: $defaultValue,
712: );
713: }
714:
715: return new self(
716: $parameters,
717: $cb($this->getReturnType(), $right->getReturnType()),
718: $this->isVariadic(),
719: $this->templateTypeMap,
720: $this->resolvedTemplateTypeMap,
721: $this->callSiteVarianceMap,
722: $this->templateTags,
723: $this->throwPoints,
724: $this->impurePoints,
725: $this->invalidateExpressions,
726: $this->usedVariables,
727: $this->acceptsNamedArguments,
728: $this->mustUseReturnValue,
729: $this->assertions,
730: );
731: }
732:
733: public function isNull(): TrinaryLogic
734: {
735: return TrinaryLogic::createNo();
736: }
737:
738: public function isConstantValue(): TrinaryLogic
739: {
740: return TrinaryLogic::createNo();
741: }
742:
743: public function isConstantScalarValue(): TrinaryLogic
744: {
745: return TrinaryLogic::createNo();
746: }
747:
748: public function getConstantScalarTypes(): array
749: {
750: return [];
751: }
752:
753: public function getConstantScalarValues(): array
754: {
755: return [];
756: }
757:
758: public function isTrue(): TrinaryLogic
759: {
760: return TrinaryLogic::createNo();
761: }
762:
763: public function isFalse(): TrinaryLogic
764: {
765: return TrinaryLogic::createNo();
766: }
767:
768: public function isBoolean(): TrinaryLogic
769: {
770: return TrinaryLogic::createNo();
771: }
772:
773: public function isFloat(): TrinaryLogic
774: {
775: return TrinaryLogic::createNo();
776: }
777:
778: public function isInteger(): TrinaryLogic
779: {
780: return TrinaryLogic::createNo();
781: }
782:
783: public function isString(): TrinaryLogic
784: {
785: return TrinaryLogic::createNo();
786: }
787:
788: public function isNumericString(): TrinaryLogic
789: {
790: return TrinaryLogic::createNo();
791: }
792:
793: public function isNonEmptyString(): TrinaryLogic
794: {
795: return TrinaryLogic::createNo();
796: }
797:
798: public function isNonFalsyString(): TrinaryLogic
799: {
800: return TrinaryLogic::createNo();
801: }
802:
803: public function isLiteralString(): TrinaryLogic
804: {
805: return TrinaryLogic::createNo();
806: }
807:
808: public function isLowercaseString(): TrinaryLogic
809: {
810: return TrinaryLogic::createNo();
811: }
812:
813: public function isClassString(): TrinaryLogic
814: {
815: return TrinaryLogic::createNo();
816: }
817:
818: public function isUppercaseString(): TrinaryLogic
819: {
820: return TrinaryLogic::createNo();
821: }
822:
823: public function getClassStringObjectType(): Type
824: {
825: return new ErrorType();
826: }
827:
828: public function getObjectTypeOrClassStringObjectType(): Type
829: {
830: return $this;
831: }
832:
833: public function isVoid(): TrinaryLogic
834: {
835: return TrinaryLogic::createNo();
836: }
837:
838: public function isScalar(): TrinaryLogic
839: {
840: return TrinaryLogic::createNo();
841: }
842:
843: public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
844: {
845: return new BooleanType();
846: }
847:
848: public function exponentiate(Type $exponent): Type
849: {
850: return new ErrorType();
851: }
852:
853: public function getFiniteTypes(): array
854: {
855: return [];
856: }
857:
858: public function toPhpDocNode(): TypeNode
859: {
860: if ($this->isCommonCallable) {
861: return new IdentifierTypeNode($this->isPure()->yes() ? 'pure-Closure' : 'Closure');
862: }
863:
864: $parameters = [];
865: foreach ($this->parameters as $parameter) {
866: $parameters[] = new CallableTypeParameterNode(
867: $parameter->getType()->toPhpDocNode(),
868: !$parameter->passedByReference()->no(),
869: $parameter->isVariadic(),
870: $parameter->getName() === '' ? '' : '$' . $parameter->getName(),
871: $parameter->isOptional(),
872: );
873: }
874:
875: $templateTags = [];
876: foreach ($this->templateTags as $templateName => $templateTag) {
877: $templateTags[] = new TemplateTagValueNode(
878: $templateName,
879: $templateTag->getBound()->toPhpDocNode(),
880: '',
881: );
882: }
883:
884: return new CallableTypeNode(
885: new IdentifierTypeNode('Closure'),
886: $parameters,
887: $this->returnType->toPhpDocNode(),
888: $templateTags,
889: );
890: }
891:
892: public function hasTemplateOrLateResolvableType(): bool
893: {
894: foreach ($this->parameters as $parameter) {
895: if ($parameter->getType()->hasTemplateOrLateResolvableType()) {
896: return true;
897: }
898:
899: if (!$parameter instanceof ExtendedParameterReflection) {
900: continue;
901: }
902:
903: if ($parameter->getOutType() !== null && $parameter->getOutType()->hasTemplateOrLateResolvableType()) {
904: return true;
905: }
906: if ($parameter->getClosureThisType() !== null && $parameter->getClosureThisType()->hasTemplateOrLateResolvableType()) {
907: return true;
908: }
909: }
910:
911: return $this->getReturnType()->hasTemplateOrLateResolvableType();
912: }
913:
914: }
915: