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