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