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