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