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: if (!$isFinal || $type instanceof ThisType) {
360: return $traverse($type);
361: }
362:
363: return $traverse($type->getStaticObjectType());
364: }
365:
366: return $traverse($type);
367: });
368: }
369:
370: public function canAccessConstants(): TrinaryLogic
371: {
372: return $this->getStaticObjectType()->canAccessConstants();
373: }
374:
375: public function hasConstant(string $constantName): TrinaryLogic
376: {
377: return $this->getStaticObjectType()->hasConstant($constantName);
378: }
379:
380: public function getConstant(string $constantName): ClassConstantReflection
381: {
382: return $this->getStaticObjectType()->getConstant($constantName);
383: }
384:
385: public function changeBaseClass(ClassReflection $classReflection): self
386: {
387: return new self($classReflection, $this->subtractedType);
388: }
389:
390: public function isIterable(): TrinaryLogic
391: {
392: return $this->getStaticObjectType()->isIterable();
393: }
394:
395: public function isIterableAtLeastOnce(): TrinaryLogic
396: {
397: return $this->getStaticObjectType()->isIterableAtLeastOnce();
398: }
399:
400: public function getArraySize(): Type
401: {
402: return $this->getStaticObjectType()->getArraySize();
403: }
404:
405: public function getIterableKeyType(): Type
406: {
407: return $this->getStaticObjectType()->getIterableKeyType();
408: }
409:
410: public function getFirstIterableKeyType(): Type
411: {
412: return $this->getStaticObjectType()->getIterableKeyType();
413: }
414:
415: public function getLastIterableKeyType(): Type
416: {
417: return $this->getStaticObjectType()->getIterableKeyType();
418: }
419:
420: public function getIterableValueType(): Type
421: {
422: return $this->getStaticObjectType()->getIterableValueType();
423: }
424:
425: public function getFirstIterableValueType(): Type
426: {
427: return $this->getStaticObjectType()->getIterableValueType();
428: }
429:
430: public function getLastIterableValueType(): Type
431: {
432: return $this->getStaticObjectType()->getIterableValueType();
433: }
434:
435: public function isOffsetAccessible(): TrinaryLogic
436: {
437: return $this->getStaticObjectType()->isOffsetAccessible();
438: }
439:
440: public function isOffsetAccessLegal(): TrinaryLogic
441: {
442: return $this->getStaticObjectType()->isOffsetAccessLegal();
443: }
444:
445: public function hasOffsetValueType(Type $offsetType): TrinaryLogic
446: {
447: return $this->getStaticObjectType()->hasOffsetValueType($offsetType);
448: }
449:
450: public function getOffsetValueType(Type $offsetType): Type
451: {
452: return $this->getStaticObjectType()->getOffsetValueType($offsetType);
453: }
454:
455: public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
456: {
457: return $this->getStaticObjectType()->setOffsetValueType($offsetType, $valueType, $unionValues);
458: }
459:
460: public function setExistingOffsetValueType(Type $offsetType, Type $valueType): Type
461: {
462: return $this->getStaticObjectType()->setExistingOffsetValueType($offsetType, $valueType);
463: }
464:
465: public function unsetOffset(Type $offsetType): Type
466: {
467: return $this->getStaticObjectType()->unsetOffset($offsetType);
468: }
469:
470: public function getKeysArrayFiltered(Type $filterValueType, TrinaryLogic $strict): Type
471: {
472: return $this->getStaticObjectType()->getKeysArrayFiltered($filterValueType, $strict);
473: }
474:
475: public function getKeysArray(): Type
476: {
477: return $this->getStaticObjectType()->getKeysArray();
478: }
479:
480: public function getValuesArray(): Type
481: {
482: return $this->getStaticObjectType()->getValuesArray();
483: }
484:
485: public function chunkArray(Type $lengthType, TrinaryLogic $preserveKeys): Type
486: {
487: return $this->getStaticObjectType()->chunkArray($lengthType, $preserveKeys);
488: }
489:
490: public function fillKeysArray(Type $valueType): Type
491: {
492: return $this->getStaticObjectType()->fillKeysArray($valueType);
493: }
494:
495: public function flipArray(): Type
496: {
497: return $this->getStaticObjectType()->flipArray();
498: }
499:
500: public function intersectKeyArray(Type $otherArraysType): Type
501: {
502: return $this->getStaticObjectType()->intersectKeyArray($otherArraysType);
503: }
504:
505: public function popArray(): Type
506: {
507: return $this->getStaticObjectType()->popArray();
508: }
509:
510: public function reverseArray(TrinaryLogic $preserveKeys): Type
511: {
512: return $this->getStaticObjectType()->reverseArray($preserveKeys);
513: }
514:
515: public function searchArray(Type $needleType, ?TrinaryLogic $strict = null): Type
516: {
517: return $this->getStaticObjectType()->searchArray($needleType, $strict);
518: }
519:
520: public function shiftArray(): Type
521: {
522: return $this->getStaticObjectType()->shiftArray();
523: }
524:
525: public function shuffleArray(): Type
526: {
527: return $this->getStaticObjectType()->shuffleArray();
528: }
529:
530: public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
531: {
532: return $this->getStaticObjectType()->sliceArray($offsetType, $lengthType, $preserveKeys);
533: }
534:
535: public function spliceArray(Type $offsetType, Type $lengthType, Type $replacementType): Type
536: {
537: return $this->getStaticObjectType()->spliceArray($offsetType, $lengthType, $replacementType);
538: }
539:
540: public function isCallable(): TrinaryLogic
541: {
542: return $this->getStaticObjectType()->isCallable();
543: }
544:
545: public function getEnumCases(): array
546: {
547: return $this->getStaticObjectType()->getEnumCases();
548: }
549:
550: public function getEnumCaseObject(): ?EnumCaseObjectType
551: {
552: return $this->getStaticObjectType()->getEnumCaseObject();
553: }
554:
555: public function isArray(): TrinaryLogic
556: {
557: return $this->getStaticObjectType()->isArray();
558: }
559:
560: public function isConstantArray(): TrinaryLogic
561: {
562: return $this->getStaticObjectType()->isConstantArray();
563: }
564:
565: public function isOversizedArray(): TrinaryLogic
566: {
567: return $this->getStaticObjectType()->isOversizedArray();
568: }
569:
570: public function isList(): TrinaryLogic
571: {
572: return $this->getStaticObjectType()->isList();
573: }
574:
575: public function isNull(): TrinaryLogic
576: {
577: return $this->getStaticObjectType()->isNull();
578: }
579:
580: public function isConstantValue(): TrinaryLogic
581: {
582: return $this->getStaticObjectType()->isConstantValue();
583: }
584:
585: public function isConstantScalarValue(): TrinaryLogic
586: {
587: return $this->getStaticObjectType()->isConstantScalarValue();
588: }
589:
590: public function getConstantScalarTypes(): array
591: {
592: return $this->getStaticObjectType()->getConstantScalarTypes();
593: }
594:
595: public function getConstantScalarValues(): array
596: {
597: return $this->getStaticObjectType()->getConstantScalarValues();
598: }
599:
600: public function isTrue(): TrinaryLogic
601: {
602: return $this->getStaticObjectType()->isTrue();
603: }
604:
605: public function isFalse(): TrinaryLogic
606: {
607: return $this->getStaticObjectType()->isFalse();
608: }
609:
610: public function isBoolean(): TrinaryLogic
611: {
612: return $this->getStaticObjectType()->isBoolean();
613: }
614:
615: public function isFloat(): TrinaryLogic
616: {
617: return $this->getStaticObjectType()->isFloat();
618: }
619:
620: public function isInteger(): TrinaryLogic
621: {
622: return $this->getStaticObjectType()->isInteger();
623: }
624:
625: public function isString(): TrinaryLogic
626: {
627: return $this->getStaticObjectType()->isString();
628: }
629:
630: public function isNumericString(): TrinaryLogic
631: {
632: return $this->getStaticObjectType()->isNumericString();
633: }
634:
635: public function isNonEmptyString(): TrinaryLogic
636: {
637: return $this->getStaticObjectType()->isNonEmptyString();
638: }
639:
640: public function isNonFalsyString(): TrinaryLogic
641: {
642: return $this->getStaticObjectType()->isNonFalsyString();
643: }
644:
645: public function isLiteralString(): TrinaryLogic
646: {
647: return $this->getStaticObjectType()->isLiteralString();
648: }
649:
650: public function isLowercaseString(): TrinaryLogic
651: {
652: return $this->getStaticObjectType()->isLowercaseString();
653: }
654:
655: public function isUppercaseString(): TrinaryLogic
656: {
657: return $this->getStaticObjectType()->isUppercaseString();
658: }
659:
660: public function isClassString(): TrinaryLogic
661: {
662: return $this->getStaticObjectType()->isClassString();
663: }
664:
665: public function getClassStringObjectType(): Type
666: {
667: return $this->getStaticObjectType()->getClassStringObjectType();
668: }
669:
670: public function getObjectTypeOrClassStringObjectType(): Type
671: {
672: return $this;
673: }
674:
675: public function isVoid(): TrinaryLogic
676: {
677: return $this->getStaticObjectType()->isVoid();
678: }
679:
680: public function isScalar(): TrinaryLogic
681: {
682: return $this->getStaticObjectType()->isScalar();
683: }
684:
685: public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
686: {
687: return new BooleanType();
688: }
689:
690: public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope): array
691: {
692: return $this->getStaticObjectType()->getCallableParametersAcceptors($scope);
693: }
694:
695: public function isCloneable(): TrinaryLogic
696: {
697: return TrinaryLogic::createYes();
698: }
699:
700: public function toNumber(): Type
701: {
702: return new ErrorType();
703: }
704:
705: public function toAbsoluteNumber(): Type
706: {
707: return new ErrorType();
708: }
709:
710: public function toString(): Type
711: {
712: return $this->getStaticObjectType()->toString();
713: }
714:
715: public function toInteger(): Type
716: {
717: return new ErrorType();
718: }
719:
720: public function toFloat(): Type
721: {
722: return new ErrorType();
723: }
724:
725: public function toArray(): Type
726: {
727: return $this->getStaticObjectType()->toArray();
728: }
729:
730: public function toArrayKey(): Type
731: {
732: return $this->getStaticObjectType()->toArrayKey();
733: }
734:
735: public function toCoercedArgumentType(bool $strictTypes): Type
736: {
737: return $this->getStaticObjectType()->toCoercedArgumentType($strictTypes);
738: }
739:
740: public function toBoolean(): BooleanType
741: {
742: return $this->getStaticObjectType()->toBoolean();
743: }
744:
745: public function traverse(callable $cb): Type
746: {
747: $subtractedType = $this->subtractedType !== null ? $cb($this->subtractedType) : null;
748:
749: if ($subtractedType !== $this->subtractedType) {
750: return new self(
751: $this->classReflection,
752: $subtractedType,
753: );
754: }
755:
756: return $this;
757: }
758:
759: public function traverseSimultaneously(Type $right, callable $cb): Type
760: {
761: if ($this->subtractedType === null) {
762: return $this;
763: }
764:
765: return new self($this->classReflection);
766: }
767:
768: public function subtract(Type $type): Type
769: {
770: if ($this->subtractedType !== null) {
771: $type = TypeCombinator::union($this->subtractedType, $type);
772: }
773:
774: return $this->changeSubtractedType($type);
775: }
776:
777: public function getTypeWithoutSubtractedType(): Type
778: {
779: return $this->changeSubtractedType(null);
780: }
781:
782: public function changeSubtractedType(?Type $subtractedType): Type
783: {
784: if ($subtractedType !== null) {
785: $classReflection = $this->getClassReflection();
786: if ($classReflection->getAllowedSubTypes() !== null) {
787: $objectType = $this->getStaticObjectType()->changeSubtractedType($subtractedType);
788: if ($objectType instanceof NeverType) {
789: return $objectType;
790: }
791:
792: if ($objectType instanceof ObjectType && $objectType->getSubtractedType() !== null) {
793: return new self($classReflection, $objectType->getSubtractedType());
794: }
795:
796: return TypeCombinator::intersect($this, $objectType);
797: }
798: }
799:
800: return new self($this->classReflection, $subtractedType);
801: }
802:
803: public function getSubtractedType(): ?Type
804: {
805: return $this->subtractedType;
806: }
807:
808: public function tryRemove(Type $typeToRemove): ?Type
809: {
810: if ($this->getStaticObjectType()->isSuperTypeOf($typeToRemove)->yes()) {
811: return $this->subtract($typeToRemove);
812: }
813:
814: return null;
815: }
816:
817: public function exponentiate(Type $exponent): Type
818: {
819: return $this->getStaticObjectType()->exponentiate($exponent);
820: }
821:
822: public function getFiniteTypes(): array
823: {
824: return $this->getStaticObjectType()->getFiniteTypes();
825: }
826:
827: public function toPhpDocNode(): TypeNode
828: {
829: return new IdentifierTypeNode('static');
830: }
831:
832: public function hasTemplateOrLateResolvableType(): bool
833: {
834: return $this->getStaticObjectType()->hasTemplateOrLateResolvableType();
835: }
836:
837: }
838: