1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type;
4:
5: use PHPStan\TrinaryLogic;
6: use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
7: use PHPStan\Type\Traits\NonGenericTypeTrait;
8: use PHPStan\Type\Traits\ObjectTypeTrait;
9: use PHPStan\Type\Traits\UndecidedComparisonTypeTrait;
10: use function sprintf;
11:
12: /** @api */
13: class ObjectWithoutClassType implements SubtractableType
14: {
15:
16: use ObjectTypeTrait;
17: use NonGenericTypeTrait;
18: use UndecidedComparisonTypeTrait;
19: use NonGeneralizableTypeTrait;
20:
21: private ?Type $subtractedType;
22:
23: /** @api */
24: public function __construct(
25: ?Type $subtractedType = null,
26: )
27: {
28: if ($subtractedType instanceof NeverType) {
29: $subtractedType = null;
30: }
31:
32: $this->subtractedType = $subtractedType;
33: }
34:
35: /**
36: * @return string[]
37: */
38: public function getReferencedClasses(): array
39: {
40: return [];
41: }
42:
43: public function accepts(Type $type, bool $strictTypes): TrinaryLogic
44: {
45: if ($type instanceof CompoundType) {
46: return $type->isAcceptedBy($this, $strictTypes);
47: }
48:
49: return TrinaryLogic::createFromBoolean(
50: $type instanceof self || $type instanceof TypeWithClassName,
51: );
52: }
53:
54: public function isSuperTypeOf(Type $type): TrinaryLogic
55: {
56: if ($type instanceof CompoundType) {
57: return $type->isSubTypeOf($this);
58: }
59:
60: if ($type instanceof self) {
61: if ($this->subtractedType === null) {
62: return TrinaryLogic::createYes();
63: }
64: if ($type->subtractedType !== null) {
65: $isSuperType = $type->subtractedType->isSuperTypeOf($this->subtractedType);
66: if ($isSuperType->yes()) {
67: return TrinaryLogic::createYes();
68: }
69: }
70:
71: return TrinaryLogic::createMaybe();
72: }
73:
74: if ($type instanceof TypeWithClassName) {
75: if ($this->subtractedType === null) {
76: return TrinaryLogic::createYes();
77: }
78:
79: return $this->subtractedType->isSuperTypeOf($type)->negate();
80: }
81:
82: return TrinaryLogic::createNo();
83: }
84:
85: public function equals(Type $type): bool
86: {
87: if (!$type instanceof self) {
88: return false;
89: }
90:
91: if ($this->subtractedType === null) {
92: if ($type->subtractedType === null) {
93: return true;
94: }
95:
96: return false;
97: }
98:
99: if ($type->subtractedType === null) {
100: return false;
101: }
102:
103: return $this->subtractedType->equals($type->subtractedType);
104: }
105:
106: public function describe(VerbosityLevel $level): string
107: {
108: return $level->handle(
109: static fn (): string => 'object',
110: static fn (): string => 'object',
111: function () use ($level): string {
112: $description = 'object';
113: if ($this->subtractedType !== null) {
114: $description .= sprintf('~%s', $this->subtractedType->describe($level));
115: }
116:
117: return $description;
118: },
119: );
120: }
121:
122: public function subtract(Type $type): Type
123: {
124: if ($type instanceof self) {
125: return new NeverType();
126: }
127: if ($this->subtractedType !== null) {
128: $type = TypeCombinator::union($this->subtractedType, $type);
129: }
130:
131: return new self($type);
132: }
133:
134: public function getTypeWithoutSubtractedType(): Type
135: {
136: return new self();
137: }
138:
139: public function changeSubtractedType(?Type $subtractedType): Type
140: {
141: return new self($subtractedType);
142: }
143:
144: public function getSubtractedType(): ?Type
145: {
146: return $this->subtractedType;
147: }
148:
149: public function traverse(callable $cb): Type
150: {
151: $subtractedType = $this->subtractedType !== null ? $cb($this->subtractedType) : null;
152:
153: if ($subtractedType !== $this->subtractedType) {
154: return new self($subtractedType);
155: }
156:
157: return $this;
158: }
159:
160: public function tryRemove(Type $typeToRemove): ?Type
161: {
162: if ($this->isSuperTypeOf($typeToRemove)->yes()) {
163: return $this->subtract($typeToRemove);
164: }
165:
166: return null;
167: }
168:
169: /**
170: * @param mixed[] $properties
171: */
172: public static function __set_state(array $properties): Type
173: {
174: return new self($properties['subtractedType'] ?? null);
175: }
176:
177: }
178: