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