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