1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type\Accessory;
4:
5: use PHPStan\Php\PhpVersion;
6: use PHPStan\TrinaryLogic;
7: use PHPStan\Type\AcceptsResult;
8: use PHPStan\Type\BooleanType;
9: use PHPStan\Type\CompoundType;
10: use PHPStan\Type\Constant\ConstantIntegerType;
11: use PHPStan\Type\Constant\ConstantStringType;
12: use PHPStan\Type\ErrorType;
13: use PHPStan\Type\IntersectionType;
14: use PHPStan\Type\MixedType;
15: use PHPStan\Type\ObjectWithoutClassType;
16: use PHPStan\Type\Traits\MaybeArrayTypeTrait;
17: use PHPStan\Type\Traits\MaybeCallableTypeTrait;
18: use PHPStan\Type\Traits\MaybeIterableTypeTrait;
19: use PHPStan\Type\Traits\MaybeObjectTypeTrait;
20: use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
21: use PHPStan\Type\Traits\NonGenericTypeTrait;
22: use PHPStan\Type\Traits\NonRemoveableTypeTrait;
23: use PHPStan\Type\Traits\TruthyBooleanTypeTrait;
24: use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
25: use PHPStan\Type\Type;
26: use PHPStan\Type\UnionType;
27: use PHPStan\Type\VerbosityLevel;
28: use function sprintf;
29:
30: class HasOffsetType implements CompoundType, AccessoryType
31: {
32:
33: use MaybeArrayTypeTrait;
34: use MaybeCallableTypeTrait;
35: use MaybeIterableTypeTrait;
36: use MaybeObjectTypeTrait;
37: use TruthyBooleanTypeTrait;
38: use NonGenericTypeTrait;
39: use UndecidedComparisonCompoundTypeTrait;
40: use NonRemoveableTypeTrait;
41: use NonGeneralizableTypeTrait;
42:
43: /**
44: * @api
45: * @param ConstantStringType|ConstantIntegerType $offsetType
46: */
47: public function __construct(private Type $offsetType)
48: {
49: }
50:
51: /**
52: * @return ConstantStringType|ConstantIntegerType
53: */
54: public function getOffsetType(): Type
55: {
56: return $this->offsetType;
57: }
58:
59: public function getReferencedClasses(): array
60: {
61: return [];
62: }
63:
64: public function getObjectClassNames(): array
65: {
66: return [];
67: }
68:
69: public function getObjectClassReflections(): array
70: {
71: return [];
72: }
73:
74: public function getConstantStrings(): array
75: {
76: return [];
77: }
78:
79: public function accepts(Type $type, bool $strictTypes): TrinaryLogic
80: {
81: return $this->acceptsWithReason($type, $strictTypes)->result;
82: }
83:
84: public function acceptsWithReason(Type $type, bool $strictTypes): AcceptsResult
85: {
86: if ($type instanceof CompoundType) {
87: return $type->isAcceptedWithReasonBy($this, $strictTypes);
88: }
89:
90: return new AcceptsResult($type->isOffsetAccessible()->and($type->hasOffsetValueType($this->offsetType)), []);
91: }
92:
93: public function isSuperTypeOf(Type $type): TrinaryLogic
94: {
95: if ($this->equals($type)) {
96: return TrinaryLogic::createYes();
97: }
98: return $type->isOffsetAccessible()
99: ->and($type->hasOffsetValueType($this->offsetType));
100: }
101:
102: public function isSubTypeOf(Type $otherType): TrinaryLogic
103: {
104: if ($otherType instanceof UnionType || $otherType instanceof IntersectionType) {
105: return $otherType->isSuperTypeOf($this);
106: }
107:
108: return $otherType->isOffsetAccessible()
109: ->and($otherType->hasOffsetValueType($this->offsetType))
110: ->and($otherType instanceof self ? TrinaryLogic::createYes() : TrinaryLogic::createMaybe());
111: }
112:
113: public function isAcceptedBy(Type $acceptingType, bool $strictTypes): TrinaryLogic
114: {
115: return $this->isAcceptedWithReasonBy($acceptingType, $strictTypes)->result;
116: }
117:
118: public function isAcceptedWithReasonBy(Type $acceptingType, bool $strictTypes): AcceptsResult
119: {
120: return new AcceptsResult($this->isSubTypeOf($acceptingType), []);
121: }
122:
123: public function equals(Type $type): bool
124: {
125: return $type instanceof self
126: && $this->offsetType->equals($type->offsetType);
127: }
128:
129: public function describe(VerbosityLevel $level): string
130: {
131: return sprintf('hasOffset(%s)', $this->offsetType->describe($level));
132: }
133:
134: public function isOffsetAccessible(): TrinaryLogic
135: {
136: return TrinaryLogic::createYes();
137: }
138:
139: public function hasOffsetValueType(Type $offsetType): TrinaryLogic
140: {
141: if ($offsetType->isConstantScalarValue()->yes() && $offsetType->equals($this->offsetType)) {
142: return TrinaryLogic::createYes();
143: }
144:
145: return TrinaryLogic::createMaybe();
146: }
147:
148: public function getOffsetValueType(Type $offsetType): Type
149: {
150: return new MixedType();
151: }
152:
153: public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
154: {
155: return $this;
156: }
157:
158: public function unsetOffset(Type $offsetType): Type
159: {
160: if ($this->offsetType->isSuperTypeOf($offsetType)->yes()) {
161: return new ErrorType();
162: }
163: return $this;
164: }
165:
166: public function fillKeysArray(Type $valueType): Type
167: {
168: return new NonEmptyArrayType();
169: }
170:
171: public function intersectKeyArray(Type $otherArraysType): Type
172: {
173: if ($otherArraysType->hasOffsetValueType($this->offsetType)->yes()) {
174: return $this;
175: }
176:
177: return new MixedType();
178: }
179:
180: public function shuffleArray(): Type
181: {
182: return new NonEmptyArrayType();
183: }
184:
185: public function isIterableAtLeastOnce(): TrinaryLogic
186: {
187: return TrinaryLogic::createYes();
188: }
189:
190: public function isList(): TrinaryLogic
191: {
192: if ($this->offsetType->isString()->yes()) {
193: return TrinaryLogic::createNo();
194: }
195:
196: return TrinaryLogic::createMaybe();
197: }
198:
199: public function isNull(): TrinaryLogic
200: {
201: return TrinaryLogic::createNo();
202: }
203:
204: public function isConstantValue(): TrinaryLogic
205: {
206: return TrinaryLogic::createNo();
207: }
208:
209: public function isConstantScalarValue(): TrinaryLogic
210: {
211: return TrinaryLogic::createNo();
212: }
213:
214: public function getConstantScalarTypes(): array
215: {
216: return [];
217: }
218:
219: public function getConstantScalarValues(): array
220: {
221: return [];
222: }
223:
224: public function isTrue(): TrinaryLogic
225: {
226: return TrinaryLogic::createNo();
227: }
228:
229: public function isFalse(): TrinaryLogic
230: {
231: return TrinaryLogic::createNo();
232: }
233:
234: public function isBoolean(): TrinaryLogic
235: {
236: return TrinaryLogic::createNo();
237: }
238:
239: public function isFloat(): TrinaryLogic
240: {
241: return TrinaryLogic::createNo();
242: }
243:
244: public function isInteger(): TrinaryLogic
245: {
246: return TrinaryLogic::createNo();
247: }
248:
249: public function isString(): TrinaryLogic
250: {
251: return TrinaryLogic::createMaybe();
252: }
253:
254: public function isNumericString(): TrinaryLogic
255: {
256: return TrinaryLogic::createMaybe();
257: }
258:
259: public function isNonEmptyString(): TrinaryLogic
260: {
261: return TrinaryLogic::createMaybe();
262: }
263:
264: public function isNonFalsyString(): TrinaryLogic
265: {
266: return TrinaryLogic::createMaybe();
267: }
268:
269: public function isLiteralString(): TrinaryLogic
270: {
271: return TrinaryLogic::createMaybe();
272: }
273:
274: public function isClassStringType(): TrinaryLogic
275: {
276: return TrinaryLogic::createMaybe();
277: }
278:
279: public function getClassStringObjectType(): Type
280: {
281: return new ObjectWithoutClassType();
282: }
283:
284: public function getObjectTypeOrClassStringObjectType(): Type
285: {
286: return new ObjectWithoutClassType();
287: }
288:
289: public function isVoid(): TrinaryLogic
290: {
291: return TrinaryLogic::createNo();
292: }
293:
294: public function isScalar(): TrinaryLogic
295: {
296: return TrinaryLogic::createMaybe();
297: }
298:
299: public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
300: {
301: return new BooleanType();
302: }
303:
304: public function getKeysArray(): Type
305: {
306: return new NonEmptyArrayType();
307: }
308:
309: public function getValuesArray(): Type
310: {
311: return new NonEmptyArrayType();
312: }
313:
314: public function toNumber(): Type
315: {
316: return new ErrorType();
317: }
318:
319: public function toInteger(): Type
320: {
321: return new ErrorType();
322: }
323:
324: public function toFloat(): Type
325: {
326: return new ErrorType();
327: }
328:
329: public function toString(): Type
330: {
331: return new ErrorType();
332: }
333:
334: public function toArray(): Type
335: {
336: return new MixedType();
337: }
338:
339: public function toArrayKey(): Type
340: {
341: return new ErrorType();
342: }
343:
344: public function getEnumCases(): array
345: {
346: return [];
347: }
348:
349: public function traverse(callable $cb): Type
350: {
351: return $this;
352: }
353:
354: public function exponentiate(Type $exponent): Type
355: {
356: return new ErrorType();
357: }
358:
359: public static function __set_state(array $properties): Type
360: {
361: return new self($properties['offsetType']);
362: }
363:
364: }
365: