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\MaybeCallableTypeTrait;
14: use PHPStan\Type\Traits\MaybeIterableTypeTrait;
15: use PHPStan\Type\Traits\MaybeObjectTypeTrait;
16: use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
17: use PHPStan\Type\Traits\NonGenericTypeTrait;
18: use PHPStan\Type\Traits\NonRemoveableTypeTrait;
19: use PHPStan\Type\Traits\TruthyBooleanTypeTrait;
20: use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
21: use PHPStan\Type\Type;
22: use PHPStan\Type\UnionType;
23: use PHPStan\Type\VerbosityLevel;
24: use function sprintf;
25:
26: class HasOffsetType implements CompoundType, AccessoryType
27: {
28:
29: use MaybeCallableTypeTrait;
30: use MaybeIterableTypeTrait;
31: use MaybeObjectTypeTrait;
32: use TruthyBooleanTypeTrait;
33: use NonGenericTypeTrait;
34: use UndecidedComparisonCompoundTypeTrait;
35: use NonRemoveableTypeTrait;
36: use NonGeneralizableTypeTrait;
37:
38: /**
39: * @api
40: * @param ConstantStringType|ConstantIntegerType $offsetType
41: */
42: public function __construct(private Type $offsetType)
43: {
44: }
45:
46: /**
47: * @return ConstantStringType|ConstantIntegerType
48: */
49: public function getOffsetType(): Type
50: {
51: return $this->offsetType;
52: }
53:
54: public function getReferencedClasses(): array
55: {
56: return [];
57: }
58:
59: public function accepts(Type $type, bool $strictTypes): TrinaryLogic
60: {
61: if ($type instanceof CompoundType) {
62: return $type->isAcceptedBy($this, $strictTypes);
63: }
64:
65: return $type->isOffsetAccessible()
66: ->and($type->hasOffsetValueType($this->offsetType));
67: }
68:
69: public function isSuperTypeOf(Type $type): TrinaryLogic
70: {
71: if ($this->equals($type)) {
72: return TrinaryLogic::createYes();
73: }
74: return $type->isOffsetAccessible()
75: ->and($type->hasOffsetValueType($this->offsetType));
76: }
77:
78: public function isSubTypeOf(Type $otherType): TrinaryLogic
79: {
80: if ($otherType instanceof UnionType || $otherType instanceof IntersectionType) {
81: return $otherType->isSuperTypeOf($this);
82: }
83:
84: return $otherType->isOffsetAccessible()
85: ->and($otherType->hasOffsetValueType($this->offsetType))
86: ->and($otherType instanceof self ? TrinaryLogic::createYes() : TrinaryLogic::createMaybe());
87: }
88:
89: public function isAcceptedBy(Type $acceptingType, bool $strictTypes): TrinaryLogic
90: {
91: return $this->isSubTypeOf($acceptingType);
92: }
93:
94: public function equals(Type $type): bool
95: {
96: return $type instanceof self
97: && $this->offsetType->equals($type->offsetType);
98: }
99:
100: public function describe(VerbosityLevel $level): string
101: {
102: return sprintf('hasOffset(%s)', $this->offsetType->describe($level));
103: }
104:
105: public function isOffsetAccessible(): TrinaryLogic
106: {
107: return TrinaryLogic::createYes();
108: }
109:
110: public function hasOffsetValueType(Type $offsetType): TrinaryLogic
111: {
112: if ($offsetType instanceof ConstantScalarType && $offsetType->equals($this->offsetType)) {
113: return TrinaryLogic::createYes();
114: }
115:
116: return TrinaryLogic::createMaybe();
117: }
118:
119: public function getOffsetValueType(Type $offsetType): Type
120: {
121: return new MixedType();
122: }
123:
124: public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
125: {
126: return $this;
127: }
128:
129: public function unsetOffset(Type $offsetType): Type
130: {
131: if ($this->offsetType->isSuperTypeOf($offsetType)->yes()) {
132: return new ErrorType();
133: }
134: return $this;
135: }
136:
137: public function isIterableAtLeastOnce(): TrinaryLogic
138: {
139: return TrinaryLogic::createYes();
140: }
141:
142: public function isArray(): TrinaryLogic
143: {
144: return TrinaryLogic::createMaybe();
145: }
146:
147: public function isOversizedArray(): TrinaryLogic
148: {
149: return TrinaryLogic::createMaybe();
150: }
151:
152: public function isString(): TrinaryLogic
153: {
154: return TrinaryLogic::createMaybe();
155: }
156:
157: public function isNumericString(): TrinaryLogic
158: {
159: return TrinaryLogic::createMaybe();
160: }
161:
162: public function isNonEmptyString(): TrinaryLogic
163: {
164: return TrinaryLogic::createMaybe();
165: }
166:
167: public function isNonFalsyString(): TrinaryLogic
168: {
169: return TrinaryLogic::createMaybe();
170: }
171:
172: public function isLiteralString(): TrinaryLogic
173: {
174: return TrinaryLogic::createMaybe();
175: }
176:
177: public function toNumber(): Type
178: {
179: return new ErrorType();
180: }
181:
182: public function toInteger(): Type
183: {
184: return new ErrorType();
185: }
186:
187: public function toFloat(): Type
188: {
189: return new ErrorType();
190: }
191:
192: public function toString(): Type
193: {
194: return new ErrorType();
195: }
196:
197: public function toArray(): Type
198: {
199: return new MixedType();
200: }
201:
202: public function traverse(callable $cb): Type
203: {
204: return $this;
205: }
206:
207: public static function __set_state(array $properties): Type
208: {
209: return new self($properties['offsetType']);
210: }
211:
212: }
213: