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