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