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