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