1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type\Accessory;
4:
5: use PHPStan\Php\PhpVersion;
6: use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
7: use PHPStan\PhpDocParser\Ast\Type\TypeNode;
8: use PHPStan\TrinaryLogic;
9: use PHPStan\Type\AcceptsResult;
10: use PHPStan\Type\BooleanType;
11: use PHPStan\Type\CompoundType;
12: use PHPStan\Type\Enum\EnumCaseObjectType;
13: use PHPStan\Type\ErrorType;
14: use PHPStan\Type\Generic\GenericClassStringType;
15: use PHPStan\Type\IntersectionType;
16: use PHPStan\Type\IsSuperTypeOfResult;
17: use PHPStan\Type\MixedType;
18: use PHPStan\Type\ObjectWithoutClassType;
19: use PHPStan\Type\Traits\MaybeCallableTypeTrait;
20: use PHPStan\Type\Traits\MaybeIterableTypeTrait;
21: use PHPStan\Type\Traits\MaybeObjectTypeTrait;
22: use PHPStan\Type\Traits\MaybeOffsetAccessibleTypeTrait;
23: use PHPStan\Type\Traits\MaybeStringTypeTrait;
24: use PHPStan\Type\Traits\NonArrayTypeTrait;
25: use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
26: use PHPStan\Type\Traits\NonGenericTypeTrait;
27: use PHPStan\Type\Traits\NonRemoveableTypeTrait;
28: use PHPStan\Type\Traits\TruthyBooleanTypeTrait;
29: use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
30: use PHPStan\Type\Type;
31: use PHPStan\Type\TypeCombinator;
32: use PHPStan\Type\UnionType;
33: use PHPStan\Type\VerbosityLevel;
34: use function sprintf;
35:
36: class HasPropertyType implements AccessoryType, CompoundType
37: {
38:
39: use MaybeCallableTypeTrait;
40: use MaybeIterableTypeTrait;
41: use MaybeObjectTypeTrait;
42: use MaybeOffsetAccessibleTypeTrait;
43: use MaybeStringTypeTrait;
44: use NonArrayTypeTrait;
45: use TruthyBooleanTypeTrait;
46: use NonGenericTypeTrait;
47: use UndecidedComparisonCompoundTypeTrait;
48: use NonRemoveableTypeTrait;
49: use NonGeneralizableTypeTrait;
50:
51: /** @api */
52: public function __construct(private string $propertyName)
53: {
54: }
55:
56: public function getReferencedClasses(): array
57: {
58: return [];
59: }
60:
61: public function getObjectClassNames(): array
62: {
63: return [];
64: }
65:
66: public function getObjectClassReflections(): array
67: {
68: return [];
69: }
70:
71: public function getClassStringType(): Type
72: {
73: return new GenericClassStringType($this);
74: }
75:
76: public function getPropertyName(): string
77: {
78: return $this->propertyName;
79: }
80:
81: public function accepts(Type $type, bool $strictTypes): AcceptsResult
82: {
83: if ($type instanceof CompoundType) {
84: return $type->isAcceptedBy($this, $strictTypes);
85: }
86:
87: return AcceptsResult::createFromBoolean($this->equals($type));
88: }
89:
90: public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
91: {
92: if ($type instanceof CompoundType) {
93: return $type->isSubTypeOf($this);
94: }
95:
96: return new IsSuperTypeOfResult(
97: $type->hasInstanceProperty($this->propertyName)->or($type->hasStaticProperty($this->propertyName)),
98: [],
99: );
100: }
101:
102: public function isSubTypeOf(Type $otherType): IsSuperTypeOfResult
103: {
104: if ($otherType instanceof UnionType || $otherType instanceof IntersectionType) {
105: return $otherType->isSuperTypeOf($this);
106: }
107:
108: if ($otherType instanceof self) {
109: $limit = TrinaryLogic::createYes();
110: } else {
111: $limit = TrinaryLogic::createMaybe();
112: }
113:
114: return new IsSuperTypeOfResult(
115: $limit->and($otherType->hasInstanceProperty($this->propertyName)->or($otherType->hasStaticProperty($this->propertyName))),
116: [],
117: );
118: }
119:
120: public function isAcceptedBy(Type $acceptingType, bool $strictTypes): AcceptsResult
121: {
122: return $this->isSubTypeOf($acceptingType)->toAcceptsResult();
123: }
124:
125: public function equals(Type $type): bool
126: {
127: return $type instanceof self
128: && $this->propertyName === $type->propertyName;
129: }
130:
131: public function describe(VerbosityLevel $level): string
132: {
133: return sprintf('hasProperty(%s)', $this->propertyName);
134: }
135:
136: public function hasProperty(string $propertyName): TrinaryLogic
137: {
138: if ($this->propertyName === $propertyName) {
139: return TrinaryLogic::createYes();
140: }
141:
142: return TrinaryLogic::createMaybe();
143: }
144:
145: public function hasInstanceProperty(string $propertyName): TrinaryLogic
146: {
147: if ($this->propertyName === $propertyName) {
148: return TrinaryLogic::createYes();
149: }
150:
151: return TrinaryLogic::createMaybe();
152: }
153:
154: public function hasStaticProperty(string $propertyName): TrinaryLogic
155: {
156: if ($this->propertyName === $propertyName) {
157: return TrinaryLogic::createYes();
158: }
159:
160: return TrinaryLogic::createMaybe();
161: }
162:
163: public function isNull(): TrinaryLogic
164: {
165: return TrinaryLogic::createNo();
166: }
167:
168: public function isConstantValue(): TrinaryLogic
169: {
170: return TrinaryLogic::createNo();
171: }
172:
173: public function isConstantScalarValue(): TrinaryLogic
174: {
175: return TrinaryLogic::createNo();
176: }
177:
178: public function getConstantScalarTypes(): array
179: {
180: return [];
181: }
182:
183: public function getConstantScalarValues(): array
184: {
185: return [];
186: }
187:
188: public function isTrue(): TrinaryLogic
189: {
190: return TrinaryLogic::createNo();
191: }
192:
193: public function isFalse(): TrinaryLogic
194: {
195: return TrinaryLogic::createNo();
196: }
197:
198: public function isBoolean(): TrinaryLogic
199: {
200: return TrinaryLogic::createNo();
201: }
202:
203: public function isFloat(): TrinaryLogic
204: {
205: return TrinaryLogic::createNo();
206: }
207:
208: public function isInteger(): TrinaryLogic
209: {
210: return TrinaryLogic::createNo();
211: }
212:
213: public function getClassStringObjectType(): Type
214: {
215: return $this;
216: }
217:
218: public function getObjectTypeOrClassStringObjectType(): Type
219: {
220: return $this;
221: }
222:
223: public function isVoid(): TrinaryLogic
224: {
225: return TrinaryLogic::createNo();
226: }
227:
228: public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
229: {
230: return new BooleanType();
231: }
232:
233: public function toNumber(): Type
234: {
235: return new ErrorType();
236: }
237:
238: public function toBitwiseNotType(): Type
239: {
240: return new ErrorType();
241: }
242:
243: public function toAbsoluteNumber(): Type
244: {
245: return new ErrorType();
246: }
247:
248: public function toString(): Type
249: {
250: return new ErrorType();
251: }
252:
253: public function toInteger(): Type
254: {
255: return new ErrorType();
256: }
257:
258: public function toFloat(): Type
259: {
260: return new ErrorType();
261: }
262:
263: public function toArray(): Type
264: {
265: return new MixedType();
266: }
267:
268: public function toArrayKey(): Type
269: {
270: return new ErrorType();
271: }
272:
273: public function toCoercedArgumentType(bool $strictTypes): Type
274: {
275: if (!$strictTypes) {
276: return TypeCombinator::union($this, $this->toString());
277: }
278:
279: return $this;
280: }
281:
282: public function getEnumCases(): array
283: {
284: return [];
285: }
286:
287: public function getEnumCaseObject(): ?EnumCaseObjectType
288: {
289: return null;
290: }
291:
292: public function traverse(callable $cb): Type
293: {
294: return $this;
295: }
296:
297: public function traverseSimultaneously(Type $right, callable $cb): Type
298: {
299: return $this;
300: }
301:
302: public function exponentiate(Type $exponent): Type
303: {
304: return new ErrorType();
305: }
306:
307: public function getFiniteTypes(): array
308: {
309: return [];
310: }
311:
312: public function getDefaultBaseType(): Type
313: {
314: return new ObjectWithoutClassType();
315: }
316:
317: public function toPhpDocNode(): TypeNode
318: {
319: return new IdentifierTypeNode(''); // no PHPDoc representation
320: }
321:
322: public function hasTemplateOrLateResolvableType(): bool
323: {
324: return false;
325: }
326:
327: }
328: