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