1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type\Accessory;
4:
5: use PHPStan\DependencyInjection\ReportUnsafeArrayStringKeyCastingToggle;
6: use PHPStan\Php\PhpVersion;
7: use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
8: use PHPStan\PhpDocParser\Ast\Type\TypeNode;
9: use PHPStan\TrinaryLogic;
10: use PHPStan\Type\AcceptsResult;
11: use PHPStan\Type\BenevolentUnionType;
12: use PHPStan\Type\BooleanType;
13: use PHPStan\Type\CompoundType;
14: use PHPStan\Type\Constant\ConstantArrayType;
15: use PHPStan\Type\Constant\ConstantBooleanType;
16: use PHPStan\Type\Constant\ConstantIntegerType;
17: use PHPStan\Type\Constant\ConstantStringType;
18: use PHPStan\Type\ErrorType;
19: use PHPStan\Type\FloatType;
20: use PHPStan\Type\GeneralizePrecision;
21: use PHPStan\Type\InstanceofDeprecated;
22: use PHPStan\Type\IntegerType;
23: use PHPStan\Type\IntersectionType;
24: use PHPStan\Type\IsSuperTypeOfResult;
25: use PHPStan\Type\StringType;
26: use PHPStan\Type\Traits\NonArrayTypeTrait;
27: use PHPStan\Type\Traits\NonCallableTypeTrait;
28: use PHPStan\Type\Traits\NonGenericTypeTrait;
29: use PHPStan\Type\Traits\NonIterableTypeTrait;
30: use PHPStan\Type\Traits\NonObjectTypeTrait;
31: use PHPStan\Type\Traits\UndecidedBooleanTypeTrait;
32: use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
33: use PHPStan\Type\Type;
34: use PHPStan\Type\TypeCombinator;
35: use PHPStan\Type\UnionType;
36: use PHPStan\Type\VerbosityLevel;
37:
38: #[InstanceofDeprecated(insteadUse: 'Type::isNumericString()')]
39: class AccessoryNumericStringType implements CompoundType, AccessoryType
40: {
41:
42: use NonArrayTypeTrait;
43: use NonCallableTypeTrait;
44: use NonObjectTypeTrait;
45: use NonIterableTypeTrait;
46: use UndecidedBooleanTypeTrait;
47: use UndecidedComparisonCompoundTypeTrait;
48: use NonGenericTypeTrait;
49:
50: /** @api */
51: public function __construct()
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 getConstantStrings(): array
71: {
72: return [];
73: }
74:
75: public function accepts(Type $type, bool $strictTypes): AcceptsResult
76: {
77: $isNumericString = $type->isNumericString();
78:
79: if ($isNumericString->yes()) {
80: return AcceptsResult::createYes();
81: }
82:
83: if ($type instanceof CompoundType) {
84: return $type->isAcceptedBy($this, $strictTypes);
85: }
86:
87: return new AcceptsResult($isNumericString, []);
88: }
89:
90: public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
91: {
92: if ($type instanceof CompoundType) {
93: return $type->isSubTypeOf($this);
94: }
95:
96: if ($this->equals($type)) {
97: return IsSuperTypeOfResult::createYes();
98: }
99:
100: return new IsSuperTypeOfResult($type->isNumericString(), []);
101: }
102:
103: public function isSubTypeOf(Type $otherType): IsSuperTypeOfResult
104: {
105: if ($otherType instanceof UnionType || $otherType instanceof IntersectionType) {
106: return $otherType->isSuperTypeOf($this);
107: }
108:
109: return new IsSuperTypeOfResult(
110: $otherType->isNumericString()->and($otherType instanceof self ? TrinaryLogic::createYes() : TrinaryLogic::createMaybe()),
111: [],
112: );
113: }
114:
115: public function isAcceptedBy(Type $acceptingType, bool $strictTypes): AcceptsResult
116: {
117: return $this->isSubTypeOf($acceptingType)->toAcceptsResult();
118: }
119:
120: public function equals(Type $type): bool
121: {
122: return $type instanceof self;
123: }
124:
125: public function describe(VerbosityLevel $level): string
126: {
127: return 'numeric-string';
128: }
129:
130: public function isOffsetAccessible(): TrinaryLogic
131: {
132: return TrinaryLogic::createYes();
133: }
134:
135: public function isOffsetAccessLegal(): TrinaryLogic
136: {
137: return TrinaryLogic::createYes();
138: }
139:
140: public function hasOffsetValueType(Type $offsetType): TrinaryLogic
141: {
142: return $offsetType->isInteger()->and(TrinaryLogic::createMaybe());
143: }
144:
145: public function getOffsetValueType(Type $offsetType): Type
146: {
147: if ($this->hasOffsetValueType($offsetType)->no()) {
148: return new ErrorType();
149: }
150:
151: return new StringType();
152: }
153:
154: public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
155: {
156: $stringOffset = (new StringType())->setOffsetValueType($offsetType, $valueType, $unionValues);
157:
158: if ($stringOffset instanceof ErrorType) {
159: return $stringOffset;
160: }
161:
162: return $this;
163: }
164:
165: public function setExistingOffsetValueType(Type $offsetType, Type $valueType): Type
166: {
167: return $this;
168: }
169:
170: public function unsetOffset(Type $offsetType): Type
171: {
172: return new ErrorType();
173: }
174:
175: public function toNumber(): Type
176: {
177: return new UnionType([
178: $this->toInteger(),
179: $this->toFloat(),
180: ]);
181: }
182:
183: public function toBitwiseNotType(): Type
184: {
185: return new IntersectionType([new StringType(), new AccessoryNonEmptyStringType()]);
186: }
187:
188: public function toAbsoluteNumber(): Type
189: {
190: return $this->toNumber()->toAbsoluteNumber();
191: }
192:
193: public function toInteger(): Type
194: {
195: return new IntegerType();
196: }
197:
198: public function toFloat(): Type
199: {
200: return new FloatType();
201: }
202:
203: public function toString(): Type
204: {
205: return $this;
206: }
207:
208: public function toArray(): Type
209: {
210: return new ConstantArrayType(
211: [new ConstantIntegerType(0)],
212: [$this],
213: [1],
214: isList: TrinaryLogic::createYes(),
215: );
216: }
217:
218: public function toArrayKey(): Type
219: {
220: $level = ReportUnsafeArrayStringKeyCastingToggle::getLevel();
221: if ($level !== ReportUnsafeArrayStringKeyCastingToggle::PREVENT) {
222: return new UnionType([
223: new IntegerType(),
224: new IntersectionType([
225: new StringType(),
226: new AccessoryNumericStringType(),
227: ]),
228: ]);
229: }
230:
231: return new UnionType([
232: new IntegerType(),
233: new IntersectionType([new StringType(), new AccessoryDecimalIntegerStringType(inverse: true)]),
234: ]);
235: }
236:
237: public function toCoercedArgumentType(bool $strictTypes): Type
238: {
239: if (!$strictTypes) {
240: return TypeCombinator::union($this->toInteger(), $this->toFloat(), $this, $this->toBoolean());
241: }
242:
243: return $this;
244: }
245:
246: public function isNull(): TrinaryLogic
247: {
248: return TrinaryLogic::createNo();
249: }
250:
251: public function isConstantValue(): TrinaryLogic
252: {
253: return TrinaryLogic::createMaybe();
254: }
255:
256: public function isConstantScalarValue(): TrinaryLogic
257: {
258: return TrinaryLogic::createMaybe();
259: }
260:
261: public function getConstantScalarTypes(): array
262: {
263: return [];
264: }
265:
266: public function getConstantScalarValues(): array
267: {
268: return [];
269: }
270:
271: public function isTrue(): TrinaryLogic
272: {
273: return TrinaryLogic::createNo();
274: }
275:
276: public function isFalse(): TrinaryLogic
277: {
278: return TrinaryLogic::createNo();
279: }
280:
281: public function isBoolean(): TrinaryLogic
282: {
283: return TrinaryLogic::createNo();
284: }
285:
286: public function isFloat(): TrinaryLogic
287: {
288: return TrinaryLogic::createNo();
289: }
290:
291: public function isInteger(): TrinaryLogic
292: {
293: return TrinaryLogic::createNo();
294: }
295:
296: public function isString(): TrinaryLogic
297: {
298: return TrinaryLogic::createYes();
299: }
300:
301: public function isNumericString(): TrinaryLogic
302: {
303: return TrinaryLogic::createYes();
304: }
305:
306: public function isDecimalIntegerString(): TrinaryLogic
307: {
308: return TrinaryLogic::createMaybe();
309: }
310:
311: public function isNonEmptyString(): TrinaryLogic
312: {
313: return TrinaryLogic::createYes();
314: }
315:
316: public function isNonFalsyString(): TrinaryLogic
317: {
318: return TrinaryLogic::createMaybe();
319: }
320:
321: public function isLiteralString(): TrinaryLogic
322: {
323: return TrinaryLogic::createMaybe();
324: }
325:
326: public function isLowercaseString(): TrinaryLogic
327: {
328: return TrinaryLogic::createMaybe();
329: }
330:
331: public function isUppercaseString(): TrinaryLogic
332: {
333: return TrinaryLogic::createMaybe();
334: }
335:
336: public function isClassString(): TrinaryLogic
337: {
338: return TrinaryLogic::createNo();
339: }
340:
341: public function getClassStringObjectType(): Type
342: {
343: return new ErrorType();
344: }
345:
346: public function getObjectTypeOrClassStringObjectType(): Type
347: {
348: return new ErrorType();
349: }
350:
351: public function isVoid(): TrinaryLogic
352: {
353: return TrinaryLogic::createNo();
354: }
355:
356: public function isScalar(): TrinaryLogic
357: {
358: return TrinaryLogic::createYes();
359: }
360:
361: public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
362: {
363: if ($type->isNull()->yes()) {
364: return new ConstantBooleanType(false);
365: }
366:
367: if ($type->isString()->yes() && $type->isNumericString()->no()) {
368: return new ConstantBooleanType(false);
369: }
370:
371: return new BooleanType();
372: }
373:
374: public function traverse(callable $cb): Type
375: {
376: return $this;
377: }
378:
379: public function traverseSimultaneously(Type $right, callable $cb): Type
380: {
381: return $this;
382: }
383:
384: public function generalize(GeneralizePrecision $precision): Type
385: {
386: return new StringType();
387: }
388:
389: public function tryRemove(Type $typeToRemove): ?Type
390: {
391: if ($typeToRemove instanceof ConstantStringType && $typeToRemove->getValue() === '0') {
392: return new IntersectionType([new StringType(), $this, new AccessoryNonFalsyStringType()]);
393: }
394:
395: return null;
396: }
397:
398: public function exponentiate(Type $exponent): Type
399: {
400: return new BenevolentUnionType([
401: new FloatType(),
402: new IntegerType(),
403: ]);
404: }
405:
406: public function getFiniteTypes(): array
407: {
408: return [];
409: }
410:
411: public function getDefaultBaseType(): Type
412: {
413: return new StringType();
414: }
415:
416: public function toPhpDocNode(): TypeNode
417: {
418: return new IdentifierTypeNode('numeric-string');
419: }
420:
421: public function hasTemplateOrLateResolvableType(): bool
422: {
423: return false;
424: }
425:
426: }
427: