1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type;
4:
5: use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
6: use PHPStan\PhpDocParser\Ast\Type\TypeNode;
7: use PHPStan\Type\Enum\EnumCaseObjectType;
8: use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
9: use PHPStan\Type\Traits\NonGenericTypeTrait;
10: use PHPStan\Type\Traits\ObjectTypeTrait;
11: use PHPStan\Type\Traits\SubstractableTypeTrait;
12: use PHPStan\Type\Traits\UndecidedComparisonTypeTrait;
13:
14: /** @api */
15: #[InstanceofDeprecated(insteadUse: 'Type::isObject()')]
16: class ObjectWithoutClassType implements SubtractableType
17: {
18:
19: use ObjectTypeTrait;
20: use NonGenericTypeTrait;
21: use UndecidedComparisonTypeTrait;
22: use NonGeneralizableTypeTrait;
23: use SubstractableTypeTrait;
24:
25: private ?Type $subtractedType;
26:
27: /** @api */
28: public function __construct(
29: ?Type $subtractedType = null,
30: )
31: {
32: if ($subtractedType instanceof NeverType) {
33: $subtractedType = null;
34: }
35:
36: $this->subtractedType = $subtractedType;
37: }
38:
39: public function getReferencedClasses(): array
40: {
41: return [];
42: }
43:
44: public function getObjectClassNames(): array
45: {
46: return [];
47: }
48:
49: public function getObjectClassReflections(): array
50: {
51: return [];
52: }
53:
54: public function getClassStringType(): Type
55: {
56: return new ClassStringType();
57: }
58:
59: public function accepts(Type $type, bool $strictTypes): AcceptsResult
60: {
61: if ($type instanceof CompoundType) {
62: return $type->isAcceptedBy($this, $strictTypes);
63: }
64:
65: return AcceptsResult::createFromBoolean(
66: $type instanceof self || $type instanceof ObjectShapeType || $type->getObjectClassNames() !== [],
67: );
68: }
69:
70: public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
71: {
72: if ($type instanceof CompoundType) {
73: return $type->isSubTypeOf($this);
74: }
75:
76: if ($type instanceof self) {
77: if ($this->subtractedType === null) {
78: return IsSuperTypeOfResult::createYes();
79: }
80: if ($type->subtractedType !== null) {
81: $isSuperType = $type->subtractedType->isSuperTypeOf($this->subtractedType);
82: if ($isSuperType->yes()) {
83: return $isSuperType;
84: }
85: }
86:
87: return IsSuperTypeOfResult::createMaybe();
88: }
89:
90: if ($type instanceof ObjectShapeType) {
91: return IsSuperTypeOfResult::createYes();
92: }
93:
94: if ($type->getObjectClassNames() === []) {
95: return IsSuperTypeOfResult::createNo();
96: }
97:
98: if ($this->subtractedType === null) {
99: return IsSuperTypeOfResult::createYes();
100: }
101:
102: return $this->subtractedType->isSuperTypeOf($type)->negate();
103: }
104:
105: public function equals(Type $type): bool
106: {
107: if (!$type instanceof self) {
108: return false;
109: }
110:
111: if ($this->subtractedType === null) {
112: if ($type->subtractedType === null) {
113: return true;
114: }
115:
116: return false;
117: }
118:
119: if ($type->subtractedType === null) {
120: return false;
121: }
122:
123: return $this->subtractedType->equals($type->subtractedType);
124: }
125:
126: public function describe(VerbosityLevel $level): string
127: {
128: return $level->handle(
129: static fn (): string => 'object',
130: static fn (): string => 'object',
131: fn (): string => 'object' . $this->describeSubtractedType($this->subtractedType, $level),
132: );
133: }
134:
135: public function getEnumCases(): array
136: {
137: return [];
138: }
139:
140: public function getEnumCaseObject(): ?EnumCaseObjectType
141: {
142: return null;
143: }
144:
145: public function subtract(Type $type): Type
146: {
147: if ($type instanceof self) {
148: return new NeverType();
149: }
150: if ($this->subtractedType !== null) {
151: $type = TypeCombinator::union($this->subtractedType, $type);
152: }
153:
154: return new self($type);
155: }
156:
157: public function getTypeWithoutSubtractedType(): Type
158: {
159: return new self();
160: }
161:
162: public function changeSubtractedType(?Type $subtractedType): Type
163: {
164: return new self($subtractedType);
165: }
166:
167: public function getSubtractedType(): ?Type
168: {
169: return $this->subtractedType;
170: }
171:
172: public function traverse(callable $cb): Type
173: {
174: $subtractedType = $this->subtractedType !== null ? $cb($this->subtractedType) : null;
175:
176: if ($subtractedType !== $this->subtractedType) {
177: return new self($subtractedType);
178: }
179:
180: return $this;
181: }
182:
183: public function traverseSimultaneously(Type $right, callable $cb): Type
184: {
185: if ($this->subtractedType === null) {
186: return $this;
187: }
188:
189: return new self();
190: }
191:
192: public function tryRemove(Type $typeToRemove): ?Type
193: {
194: if ($this->isSuperTypeOf($typeToRemove)->yes()) {
195: return $this->subtract($typeToRemove);
196: }
197:
198: return null;
199: }
200:
201: public function exponentiate(Type $exponent): Type
202: {
203: if (!$exponent instanceof NeverType && !$this->isSuperTypeOf($exponent)->no()) {
204: return TypeCombinator::union($this, $exponent);
205: }
206:
207: return new BenevolentUnionType([
208: new FloatType(),
209: new IntegerType(),
210: ]);
211: }
212:
213: public function getFiniteTypes(): array
214: {
215: return [];
216: }
217:
218: public function toPhpDocNode(): TypeNode
219: {
220: return new IdentifierTypeNode('object');
221: }
222:
223: public function hasTemplateOrLateResolvableType(): bool
224: {
225: return false;
226: }
227:
228: }
229: