1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type;
4:
5: use PhpParser\Node\Name;
6: use PHPStan\Php\PhpVersion;
7: use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
8: use PHPStan\PhpDocParser\Ast\Type\TypeNode;
9: use PHPStan\Reflection\ClassConstantReflection;
10: use PHPStan\Reflection\ClassMemberAccessAnswerer;
11: use PHPStan\Reflection\ClassReflection;
12: use PHPStan\Reflection\ExtendedMethodReflection;
13: use PHPStan\Reflection\ExtendedPropertyReflection;
14: use PHPStan\Reflection\ReflectionProvider;
15: use PHPStan\Reflection\Type\CallbackUnresolvedMethodPrototypeReflection;
16: use PHPStan\Reflection\Type\CallbackUnresolvedPropertyPrototypeReflection;
17: use PHPStan\Reflection\Type\UnresolvedMethodPrototypeReflection;
18: use PHPStan\Reflection\Type\UnresolvedPropertyPrototypeReflection;
19: use PHPStan\TrinaryLogic;
20: use PHPStan\Turbo\ShadowedByTurboExtension;
21: use PHPStan\Type\Accessory\AccessoryLiteralStringType;
22: use PHPStan\Type\Enum\EnumCaseObjectType;
23: use PHPStan\Type\Generic\GenericClassStringType;
24: use PHPStan\Type\Generic\GenericObjectType;
25: use PHPStan\Type\Generic\TemplateTypeHelper;
26: use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
27: use PHPStan\Type\Traits\NonGenericTypeTrait;
28: use PHPStan\Type\Traits\UndecidedComparisonTypeTrait;
29: use function get_class;
30: use function sprintf;
31:
32: /** @api */
33: #[ShadowedByTurboExtension(implementation: __DIR__ . '/../../turbo-ext/src/StaticType.cpp')]
34: class StaticType implements TypeWithClassName, SubtractableType
35: {
36:
37: use NonGenericTypeTrait;
38: use UndecidedComparisonTypeTrait;
39: use NonGeneralizableTypeTrait;
40:
41: private ?Type $subtractedType;
42:
43: private ?ObjectType $staticObjectType = null;
44:
45: private string $baseClass;
46:
47: /** @var array<string, ExtendedMethodReflection> */
48: private array $methodCache = [];
49:
50: /**
51: * @api
52: */
53: public function __construct(
54: private ClassReflection $classReflection,
55: ?Type $subtractedType = null,
56: )
57: {
58: if ($subtractedType instanceof NeverType) {
59: $subtractedType = null;
60: }
61:
62: $this->subtractedType = $subtractedType;
63: $this->baseClass = $classReflection->getName();
64: }
65:
66: public function getClassName(): string
67: {
68: return $this->baseClass;
69: }
70:
71: public function getClassReflection(): ClassReflection
72: {
73: return $this->classReflection;
74: }
75:
76: public function getAncestorWithClassName(string $className): ?TypeWithClassName
77: {
78: $ancestor = $this->getStaticObjectType()->getAncestorWithClassName($className);
79: if ($ancestor === null) {
80: return null;
81: }
82:
83: $classReflection = $ancestor->getClassReflection();
84: if ($classReflection !== null) {
85: return $this->changeBaseClass($classReflection);
86: }
87:
88: return null;
89: }
90:
91: public function getStaticObjectType(): ObjectType
92: {
93: if ($this->staticObjectType === null) {
94: if ($this->classReflection->isGeneric()) {
95: $typeMap = $this->classReflection->getActiveTemplateTypeMap()->map(static fn (string $name, Type $type): Type => TemplateTypeHelper::toArgument($type));
96: $varianceMap = $this->classReflection->getCallSiteVarianceMap();
97: return $this->staticObjectType = new GenericObjectType(
98: $this->classReflection->getName(),
99: $this->classReflection->typeMapToList($typeMap),
100: $this->subtractedType,
101: variances: $this->classReflection->varianceMapToList($varianceMap),
102: );
103: }
104:
105: return $this->staticObjectType = new ObjectType($this->classReflection->getName(), $this->subtractedType, $this->classReflection);
106: }
107:
108: return $this->staticObjectType;
109: }
110:
111: public function getReferencedClasses(): array
112: {
113: return $this->getStaticObjectType()->getReferencedClasses();
114: }
115:
116: public function getObjectClassNames(): array
117: {
118: return $this->getStaticObjectType()->getObjectClassNames();
119: }
120:
121: public function getObjectClassReflections(): array
122: {
123: return $this->getStaticObjectType()->getObjectClassReflections();
124: }
125:
126: public function getArrays(): array
127: {
128: return $this->getStaticObjectType()->getArrays();
129: }
130:
131: public function getConstantArrays(): array
132: {
133: return $this->getStaticObjectType()->getConstantArrays();
134: }
135:
136: public function getConstantStrings(): array
137: {
138: return $this->getStaticObjectType()->getConstantStrings();
139: }
140:
141: public function accepts(Type $type, bool $strictTypes): AcceptsResult
142: {
143: if ($type instanceof CompoundType) {
144: return $type->isAcceptedBy($this, $strictTypes);
145: }
146:
147: if (!$type instanceof static) {
148: return AcceptsResult::createNo();
149: }
150:
151: return $this->getStaticObjectType()->accepts($type->getStaticObjectType(), $strictTypes);
152: }
153:
154: public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
155: {
156: if ($type instanceof self) {
157: return $this->getStaticObjectType()->isSuperTypeOf($type);
158: }
159:
160: if ($type instanceof ObjectWithoutClassType) {
161: return IsSuperTypeOfResult::createMaybe();
162: }
163:
164: if ($type instanceof ObjectType) {
165: $result = $this->getStaticObjectType()->isSuperTypeOf($type);
166: if ($result->yes()) {
167: $classReflection = $type->getClassReflection();
168: if ($classReflection !== null && $classReflection->isFinal()) {
169: return $result;
170: }
171: }
172:
173: return $result->and(IsSuperTypeOfResult::createMaybe());
174: }
175:
176: if ($type instanceof CompoundType) {
177: return $type->isSubTypeOf($this);
178: }
179:
180: return IsSuperTypeOfResult::createNo();
181: }
182:
183: public function equals(Type $type): bool
184: {
185: if (get_class($type) !== static::class) {
186: return false;
187: }
188:
189: return $this->getStaticObjectType()->equals($type->getStaticObjectType());
190: }
191:
192: public function describe(VerbosityLevel $level): string
193: {
194: return sprintf('static(%s)', $this->getStaticObjectType()->describe($level));
195: }
196:
197: public function getTemplateType(string $ancestorClassName, string $templateTypeName): Type
198: {
199: return $this->getStaticObjectType()->getTemplateType($ancestorClassName, $templateTypeName);
200: }
201:
202: public function isObject(): TrinaryLogic
203: {
204: return $this->getStaticObjectType()->isObject();
205: }
206:
207: public function getClassStringType(): Type
208: {
209: return new GenericClassStringType($this);
210: }
211:
212: public function isEnum(): TrinaryLogic
213: {
214: return $this->getStaticObjectType()->isEnum();
215: }
216:
217: public function canAccessProperties(): TrinaryLogic
218: {
219: return $this->getStaticObjectType()->canAccessProperties();
220: }
221:
222: public function hasProperty(string $propertyName): TrinaryLogic
223: {
224: return $this->getStaticObjectType()->hasProperty($propertyName);
225: }
226:
227: public function getProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
228: {
229: return $this->getUnresolvedPropertyPrototype($propertyName, $scope)->getTransformedProperty();
230: }
231:
232: public function getUnresolvedPropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection
233: {
234: $staticObject = $this->getStaticObjectType();
235: $nakedProperty = $staticObject->getUnresolvedPropertyPrototype($propertyName, $scope)->getNakedProperty();
236:
237: $ancestor = $this->getAncestorWithClassName($nakedProperty->getDeclaringClass()->getName());
238: $classReflection = null;
239: if ($ancestor !== null) {
240: $classReflection = $ancestor->getClassReflection();
241: }
242: if ($classReflection === null) {
243: $classReflection = $nakedProperty->getDeclaringClass();
244: }
245:
246: return new CallbackUnresolvedPropertyPrototypeReflection(
247: $nakedProperty,
248: $classReflection,
249: false,
250: fn (Type $type): Type => $this->transformStaticType($type, $scope),
251: );
252: }
253:
254: public function hasInstanceProperty(string $propertyName): TrinaryLogic
255: {
256: return $this->getStaticObjectType()->hasInstanceProperty($propertyName);
257: }
258:
259: public function getInstanceProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
260: {
261: return $this->getUnresolvedInstancePropertyPrototype($propertyName, $scope)->getTransformedProperty();
262: }
263:
264: public function getUnresolvedInstancePropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection
265: {
266: $staticObject = $this->getStaticObjectType();
267: $nakedProperty = $staticObject->getUnresolvedInstancePropertyPrototype($propertyName, $scope)->getNakedProperty();
268:
269: $ancestor = $this->getAncestorWithClassName($nakedProperty->getDeclaringClass()->getName());
270: $classReflection = null;
271: if ($ancestor !== null) {
272: $classReflection = $ancestor->getClassReflection();
273: }
274: if ($classReflection === null) {
275: $classReflection = $nakedProperty->getDeclaringClass();
276: }
277:
278: return new CallbackUnresolvedPropertyPrototypeReflection(
279: $nakedProperty,
280: $classReflection,
281: false,
282: fn (Type $type): Type => $this->transformStaticType($type, $scope),
283: );
284: }
285:
286: public function hasStaticProperty(string $propertyName): TrinaryLogic
287: {
288: return $this->getStaticObjectType()->hasStaticProperty($propertyName);
289: }
290:
291: public function getStaticProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
292: {
293: return $this->getUnresolvedStaticPropertyPrototype($propertyName, $scope)->getTransformedProperty();
294: }
295:
296: public function getUnresolvedStaticPropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection
297: {
298: $staticObject = $this->getStaticObjectType();
299: $nakedProperty = $staticObject->getUnresolvedStaticPropertyPrototype($propertyName, $scope)->getNakedProperty();
300:
301: $ancestor = $this->getAncestorWithClassName($nakedProperty->getDeclaringClass()->getName());
302: $classReflection = null;
303: if ($ancestor !== null) {
304: $classReflection = $ancestor->getClassReflection();
305: }
306: if ($classReflection === null) {
307: $classReflection = $nakedProperty->getDeclaringClass();
308: }
309:
310: return new CallbackUnresolvedPropertyPrototypeReflection(
311: $nakedProperty,
312: $classReflection,
313: false,
314: fn (Type $type): Type => $this->transformStaticType($type, $scope),
315: );
316: }
317:
318: public function canCallMethods(): TrinaryLogic
319: {
320: return $this->getStaticObjectType()->canCallMethods();
321: }
322:
323: public function hasMethod(string $methodName): TrinaryLogic
324: {
325: return $this->getStaticObjectType()->hasMethod($methodName);
326: }
327:
328: public function getMethod(string $methodName, ClassMemberAccessAnswerer $scope): ExtendedMethodReflection
329: {
330: $key = $methodName;
331: if ($scope->isInClass()) {
332: $key = sprintf('%s-%s', $key, $scope->getClassReflection()->getCacheKey());
333: }
334: return $this->methodCache[$key] ??= $this->getUnresolvedMethodPrototype($methodName, $scope)->getTransformedMethod();
335: }
336:
337: public function getUnresolvedMethodPrototype(string $methodName, ClassMemberAccessAnswerer $scope): UnresolvedMethodPrototypeReflection
338: {
339: $staticObject = $this->getStaticObjectType();
340: $nakedMethod = $staticObject->getUnresolvedMethodPrototype($methodName, $scope)->getNakedMethod();
341:
342: $ancestor = $this->getAncestorWithClassName($nakedMethod->getDeclaringClass()->getName());
343: $classReflection = null;
344: if ($ancestor !== null) {
345: $classReflection = $ancestor->getClassReflection();
346: }
347: if ($classReflection === null) {
348: $classReflection = $nakedMethod->getDeclaringClass();
349: }
350:
351: return new CallbackUnresolvedMethodPrototypeReflection(
352: $nakedMethod,
353: $classReflection,
354: false,
355: fn (Type $type): Type => $this->transformStaticType($type, $scope),
356: );
357: }
358:
359: private function transformStaticType(Type $type, ClassMemberAccessAnswerer $scope): Type
360: {
361: return TypeTraverser::map($type, function (Type $type, callable $traverse) use ($scope): Type {
362: if ($type instanceof StaticType) {
363: $classReflection = $this->classReflection;
364: $isFinal = false;
365: if ($scope->isInClass()) {
366: $classReflection = $scope->getClassReflection();
367: $isFinal = $classReflection->isFinal();
368: }
369: $type = $type->changeBaseClass($classReflection);
370:
371: // When calling a method on a `static` type (not `$this`),
372: // `$this` return type should be downgraded to `static`
373: // because we can't guarantee the exact instance.
374: if ($type instanceof ThisType && !$this instanceof ThisType) {
375: $type = new self($type->getClassReflection(), $type->getSubtractedType());
376: }
377:
378: if ($this->getSubtractedType() !== null) {
379: $type = $type->subtract($this->getSubtractedType());
380: if (!$type instanceof StaticType) {
381: return $traverse($type);
382: }
383: }
384:
385: if (!$isFinal || $type instanceof ThisType) {
386: return RecursionGuard::run($type, static fn () => $traverse($type));
387: }
388:
389: return $traverse($type->getStaticObjectType());
390: }
391:
392: return $traverse($type);
393: });
394: }
395:
396: public function canAccessConstants(): TrinaryLogic
397: {
398: return $this->getStaticObjectType()->canAccessConstants();
399: }
400:
401: public function hasConstant(string $constantName): TrinaryLogic
402: {
403: return $this->getStaticObjectType()->hasConstant($constantName);
404: }
405:
406: public function getConstant(string $constantName): ClassConstantReflection
407: {
408: return $this->getStaticObjectType()->getConstant($constantName);
409: }
410:
411: public function changeBaseClass(ClassReflection $classReflection): self
412: {
413: return new self($classReflection, $this->subtractedType);
414: }
415:
416: public function isIterable(): TrinaryLogic
417: {
418: return $this->getStaticObjectType()->isIterable();
419: }
420:
421: public function isIterableAtLeastOnce(): TrinaryLogic
422: {
423: return $this->getStaticObjectType()->isIterableAtLeastOnce();
424: }
425:
426: public function getArraySize(): Type
427: {
428: return $this->getStaticObjectType()->getArraySize();
429: }
430:
431: public function getIterableKeyType(): Type
432: {
433: return $this->getStaticObjectType()->getIterableKeyType();
434: }
435:
436: public function getFirstIterableKeyType(): Type
437: {
438: return $this->getStaticObjectType()->getIterableKeyType();
439: }
440:
441: public function getLastIterableKeyType(): Type
442: {
443: return $this->getStaticObjectType()->getIterableKeyType();
444: }
445:
446: public function getIterableValueType(): Type
447: {
448: return $this->getStaticObjectType()->getIterableValueType();
449: }
450:
451: public function getFirstIterableValueType(): Type
452: {
453: return $this->getStaticObjectType()->getIterableValueType();
454: }
455:
456: public function getLastIterableValueType(): Type
457: {
458: return $this->getStaticObjectType()->getIterableValueType();
459: }
460:
461: public function isOffsetAccessible(): TrinaryLogic
462: {
463: return $this->getStaticObjectType()->isOffsetAccessible();
464: }
465:
466: public function isOffsetAccessLegal(): TrinaryLogic
467: {
468: return $this->getStaticObjectType()->isOffsetAccessLegal();
469: }
470:
471: public function hasOffsetValueType(Type $offsetType): TrinaryLogic
472: {
473: return $this->getStaticObjectType()->hasOffsetValueType($offsetType);
474: }
475:
476: public function getOffsetValueType(Type $offsetType): Type
477: {
478: return $this->getStaticObjectType()->getOffsetValueType($offsetType);
479: }
480:
481: public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
482: {
483: return $this->getStaticObjectType()->setOffsetValueType($offsetType, $valueType, $unionValues);
484: }
485:
486: public function setExistingOffsetValueType(Type $offsetType, Type $valueType): Type
487: {
488: return $this->getStaticObjectType()->setExistingOffsetValueType($offsetType, $valueType);
489: }
490:
491: public function unsetOffset(Type $offsetType): Type
492: {
493: return $this->getStaticObjectType()->unsetOffset($offsetType);
494: }
495:
496: public function getKeysArrayFiltered(Type $filterValueType, TrinaryLogic $strict): Type
497: {
498: return $this->getStaticObjectType()->getKeysArrayFiltered($filterValueType, $strict);
499: }
500:
501: public function getKeysArray(): Type
502: {
503: return $this->getStaticObjectType()->getKeysArray();
504: }
505:
506: public function getValuesArray(): Type
507: {
508: return $this->getStaticObjectType()->getValuesArray();
509: }
510:
511: public function chunkArray(Type $lengthType, TrinaryLogic $preserveKeys): Type
512: {
513: return $this->getStaticObjectType()->chunkArray($lengthType, $preserveKeys);
514: }
515:
516: public function fillKeysArray(Type $valueType): Type
517: {
518: return $this->getStaticObjectType()->fillKeysArray($valueType);
519: }
520:
521: public function flipArray(): Type
522: {
523: return $this->getStaticObjectType()->flipArray();
524: }
525:
526: public function intersectKeyArray(Type $otherArraysType): Type
527: {
528: return $this->getStaticObjectType()->intersectKeyArray($otherArraysType);
529: }
530:
531: public function popArray(): Type
532: {
533: return $this->getStaticObjectType()->popArray();
534: }
535:
536: public function reverseArray(TrinaryLogic $preserveKeys): Type
537: {
538: return $this->getStaticObjectType()->reverseArray($preserveKeys);
539: }
540:
541: public function searchArray(Type $needleType, ?TrinaryLogic $strict = null): Type
542: {
543: return $this->getStaticObjectType()->searchArray($needleType, $strict);
544: }
545:
546: public function shiftArray(): Type
547: {
548: return $this->getStaticObjectType()->shiftArray();
549: }
550:
551: public function shuffleArray(): Type
552: {
553: return $this->getStaticObjectType()->shuffleArray();
554: }
555:
556: public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
557: {
558: return $this->getStaticObjectType()->sliceArray($offsetType, $lengthType, $preserveKeys);
559: }
560:
561: public function spliceArray(Type $offsetType, Type $lengthType, Type $replacementType): Type
562: {
563: return $this->getStaticObjectType()->spliceArray($offsetType, $lengthType, $replacementType);
564: }
565:
566: public function truncateListToSize(Type $sizeType): Type
567: {
568: return $this->getStaticObjectType()->truncateListToSize($sizeType);
569: }
570:
571: public function makeListMaybe(): Type
572: {
573: return $this->getStaticObjectType()->makeListMaybe();
574: }
575:
576: public function mapValueType(callable $cb): Type
577: {
578: return $this->getStaticObjectType()->mapValueType($cb);
579: }
580:
581: public function mapKeyType(callable $cb): Type
582: {
583: return $this->getStaticObjectType()->mapKeyType($cb);
584: }
585:
586: public function makeAllArrayKeysOptional(): Type
587: {
588: return $this->getStaticObjectType()->makeAllArrayKeysOptional();
589: }
590:
591: public function changeKeyCaseArray(?int $case): Type
592: {
593: return $this->getStaticObjectType()->changeKeyCaseArray($case);
594: }
595:
596: public function filterArrayRemovingFalsey(): Type
597: {
598: return $this->getStaticObjectType()->filterArrayRemovingFalsey();
599: }
600:
601: public function isCallable(): TrinaryLogic
602: {
603: return $this->getStaticObjectType()->isCallable();
604: }
605:
606: public function getEnumCases(): array
607: {
608: return $this->getStaticObjectType()->getEnumCases();
609: }
610:
611: public function getEnumCaseObject(): ?EnumCaseObjectType
612: {
613: return $this->getStaticObjectType()->getEnumCaseObject();
614: }
615:
616: public function isArray(): TrinaryLogic
617: {
618: return $this->getStaticObjectType()->isArray();
619: }
620:
621: public function isConstantArray(): TrinaryLogic
622: {
623: return $this->getStaticObjectType()->isConstantArray();
624: }
625:
626: public function isOversizedArray(): TrinaryLogic
627: {
628: return $this->getStaticObjectType()->isOversizedArray();
629: }
630:
631: public function isList(): TrinaryLogic
632: {
633: return $this->getStaticObjectType()->isList();
634: }
635:
636: public function isNull(): TrinaryLogic
637: {
638: return $this->getStaticObjectType()->isNull();
639: }
640:
641: public function isConstantValue(): TrinaryLogic
642: {
643: return $this->getStaticObjectType()->isConstantValue();
644: }
645:
646: public function isConstantScalarValue(): TrinaryLogic
647: {
648: return $this->getStaticObjectType()->isConstantScalarValue();
649: }
650:
651: public function getConstantScalarTypes(): array
652: {
653: return $this->getStaticObjectType()->getConstantScalarTypes();
654: }
655:
656: public function getConstantScalarValues(): array
657: {
658: return $this->getStaticObjectType()->getConstantScalarValues();
659: }
660:
661: public function isTrue(): TrinaryLogic
662: {
663: return $this->getStaticObjectType()->isTrue();
664: }
665:
666: public function isFalse(): TrinaryLogic
667: {
668: return $this->getStaticObjectType()->isFalse();
669: }
670:
671: public function isBoolean(): TrinaryLogic
672: {
673: return $this->getStaticObjectType()->isBoolean();
674: }
675:
676: public function isFloat(): TrinaryLogic
677: {
678: return $this->getStaticObjectType()->isFloat();
679: }
680:
681: public function isInteger(): TrinaryLogic
682: {
683: return $this->getStaticObjectType()->isInteger();
684: }
685:
686: public function isString(): TrinaryLogic
687: {
688: return $this->getStaticObjectType()->isString();
689: }
690:
691: public function isNumericString(): TrinaryLogic
692: {
693: return $this->getStaticObjectType()->isNumericString();
694: }
695:
696: public function isDecimalIntegerString(): TrinaryLogic
697: {
698: return $this->getStaticObjectType()->isDecimalIntegerString();
699: }
700:
701: public function isNonEmptyString(): TrinaryLogic
702: {
703: return $this->getStaticObjectType()->isNonEmptyString();
704: }
705:
706: public function isNonFalsyString(): TrinaryLogic
707: {
708: return $this->getStaticObjectType()->isNonFalsyString();
709: }
710:
711: public function isLiteralString(): TrinaryLogic
712: {
713: return $this->getStaticObjectType()->isLiteralString();
714: }
715:
716: public function isLowercaseString(): TrinaryLogic
717: {
718: return $this->getStaticObjectType()->isLowercaseString();
719: }
720:
721: public function isUppercaseString(): TrinaryLogic
722: {
723: return $this->getStaticObjectType()->isUppercaseString();
724: }
725:
726: public function isClassString(): TrinaryLogic
727: {
728: return $this->getStaticObjectType()->isClassString();
729: }
730:
731: public function getClassStringObjectType(): Type
732: {
733: return $this->getStaticObjectType()->getClassStringObjectType();
734: }
735:
736: public function getObjectTypeOrClassStringObjectType(): Type
737: {
738: return $this;
739: }
740:
741: public function isVoid(): TrinaryLogic
742: {
743: return $this->getStaticObjectType()->isVoid();
744: }
745:
746: public function isScalar(): TrinaryLogic
747: {
748: return $this->getStaticObjectType()->isScalar();
749: }
750:
751: public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
752: {
753: return new BooleanType();
754: }
755:
756: public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope): array
757: {
758: return $this->getStaticObjectType()->getCallableParametersAcceptors($scope);
759: }
760:
761: public function isCloneable(): TrinaryLogic
762: {
763: return TrinaryLogic::createYes();
764: }
765:
766: public function toNumber(): Type
767: {
768: return new ErrorType();
769: }
770:
771: public function toBitwiseNotType(): Type
772: {
773: return new ErrorType();
774: }
775:
776: public function toGetClassResultType(): Type
777: {
778: // Preserve static binding (`class-string<static>` /
779: // `class-string<$this>`) by going through `getClassStringType()`
780: // directly instead of delegating to the underlying object type,
781: // which would resolve `static` away.
782: return $this->getClassStringType();
783: }
784:
785: public function toClassConstantType(ReflectionProvider $reflectionProvider): Type
786: {
787: // Like `toGetClassResultType()`, project through this `StaticType`'s
788: // own `getClassStringType()` so that `static::class` reads as
789: // `class-string<static>` rather than the underlying class.
790: return new IntersectionType([$this->getClassStringType(), new AccessoryLiteralStringType()]);
791: }
792:
793: public function toObjectTypeForInstanceofCheck(): ClassNameToObjectTypeResult
794: {
795: return new ClassNameToObjectTypeResult($this, true);
796: }
797:
798: public function toObjectTypeForIsACheck(Type $objectOrClassType, bool $allowString, bool $allowSameClass): ClassNameToObjectTypeResult
799: {
800: if ($allowString) {
801: return new ClassNameToObjectTypeResult(
802: new UnionType([new ObjectWithoutClassType(), new ClassStringType()]),
803: false,
804: );
805: }
806:
807: return new ClassNameToObjectTypeResult(new ObjectWithoutClassType(), false);
808: }
809:
810: public function toAbsoluteNumber(): Type
811: {
812: return new ErrorType();
813: }
814:
815: public function toString(): Type
816: {
817: return $this->getStaticObjectType()->toString();
818: }
819:
820: public function toInteger(): Type
821: {
822: return new ErrorType();
823: }
824:
825: public function toFloat(): Type
826: {
827: return new ErrorType();
828: }
829:
830: public function toArray(): Type
831: {
832: return $this->getStaticObjectType()->toArray();
833: }
834:
835: public function toArrayKey(): Type
836: {
837: return $this->getStaticObjectType()->toArrayKey();
838: }
839:
840: public function toCoercedArgumentType(bool $strictTypes): Type
841: {
842: return $this->getStaticObjectType()->toCoercedArgumentType($strictTypes);
843: }
844:
845: public function toBoolean(): BooleanType
846: {
847: return $this->getStaticObjectType()->toBoolean();
848: }
849:
850: public function traverse(callable $cb): Type
851: {
852: $subtractedType = $this->subtractedType !== null ? $cb($this->subtractedType) : null;
853:
854: if ($subtractedType !== $this->subtractedType) {
855: return new self(
856: $this->classReflection,
857: $subtractedType,
858: );
859: }
860:
861: return $this;
862: }
863:
864: public function traverseSimultaneously(Type $right, callable $cb): Type
865: {
866: if ($this->subtractedType === null) {
867: return $this;
868: }
869:
870: return new self($this->classReflection);
871: }
872:
873: public function subtract(Type $type): Type
874: {
875: if ($this->subtractedType !== null) {
876: $type = TypeCombinator::union($this->subtractedType, $type);
877: }
878:
879: return $this->changeSubtractedType($type);
880: }
881:
882: public function getTypeWithoutSubtractedType(): Type
883: {
884: return $this->changeSubtractedType(null);
885: }
886:
887: public function changeSubtractedType(?Type $subtractedType): Type
888: {
889: if ($subtractedType !== null) {
890: $classReflection = $this->getClassReflection();
891: if ($classReflection->getAllowedSubTypes() !== null) {
892: $objectType = $this->getStaticObjectType()->changeSubtractedType($subtractedType);
893: if ($objectType instanceof NeverType) {
894: return $objectType;
895: }
896:
897: if ($objectType instanceof ObjectType && $objectType->getSubtractedType() !== null) {
898: return new self($classReflection, $objectType->getSubtractedType());
899: }
900:
901: return TypeCombinator::intersect($this, $objectType);
902: }
903: }
904:
905: return new self($this->classReflection, $subtractedType);
906: }
907:
908: public function getSubtractedType(): ?Type
909: {
910: return $this->subtractedType;
911: }
912:
913: public function tryRemove(Type $typeToRemove): ?Type
914: {
915: if ($this->getStaticObjectType()->isSuperTypeOf($typeToRemove)->yes()) {
916: return $this->subtract($typeToRemove);
917: }
918:
919: return null;
920: }
921:
922: public function exponentiate(Type $exponent): Type
923: {
924: return $this->getStaticObjectType()->exponentiate($exponent);
925: }
926:
927: public function getFiniteTypes(): array
928: {
929: return $this->getStaticObjectType()->getFiniteTypes();
930: }
931:
932: public function toPhpDocNode(): TypeNode
933: {
934: return new IdentifierTypeNode('static');
935: }
936:
937: public function toNativePhpTypeNode(NativeTypePosition $position)
938: {
939: if ($position->isStaticAllowed()) {
940: return new Name('static');
941: }
942:
943: return $position->isClassNameAllowed() ? new Name('self') : null;
944: }
945:
946: public function hasTemplateOrLateResolvableType(): bool
947: {
948: return $this->getStaticObjectType()->hasTemplateOrLateResolvableType();
949: }
950:
951: }
952: