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