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