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