1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type;
4:
5: use PHPStan\Reflection\ReflectionProviderStaticAccessor;
6: use PHPStan\TrinaryLogic;
7: use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
8: use PHPStan\Type\Constant\ConstantArrayType;
9: use PHPStan\Type\Constant\ConstantIntegerType;
10: use PHPStan\Type\Constant\ConstantStringType;
11: use PHPStan\Type\Traits\MaybeCallableTypeTrait;
12: use PHPStan\Type\Traits\NonArrayTypeTrait;
13: use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
14: use PHPStan\Type\Traits\NonGenericTypeTrait;
15: use PHPStan\Type\Traits\NonIterableTypeTrait;
16: use PHPStan\Type\Traits\NonObjectTypeTrait;
17: use PHPStan\Type\Traits\UndecidedBooleanTypeTrait;
18: use PHPStan\Type\Traits\UndecidedComparisonTypeTrait;
19:
20: /** @api */
21: class StringType implements Type
22: {
23:
24: use JustNullableTypeTrait;
25: use MaybeCallableTypeTrait;
26: use NonArrayTypeTrait;
27: use NonIterableTypeTrait;
28: use NonObjectTypeTrait;
29: use UndecidedBooleanTypeTrait;
30: use UndecidedComparisonTypeTrait;
31: use NonGenericTypeTrait;
32: use NonGeneralizableTypeTrait;
33:
34: /** @api */
35: public function __construct()
36: {
37: }
38:
39: public function describe(VerbosityLevel $level): string
40: {
41: return 'string';
42: }
43:
44: public function getConstantStrings(): array
45: {
46: return [];
47: }
48:
49: public function isOffsetAccessible(): TrinaryLogic
50: {
51: return TrinaryLogic::createYes();
52: }
53:
54: public function hasOffsetValueType(Type $offsetType): TrinaryLogic
55: {
56: return (new IntegerType())->isSuperTypeOf($offsetType)->and(TrinaryLogic::createMaybe());
57: }
58:
59: public function getOffsetValueType(Type $offsetType): Type
60: {
61: if ($this->hasOffsetValueType($offsetType)->no()) {
62: return new ErrorType();
63: }
64:
65: return new StringType();
66: }
67:
68: public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
69: {
70: if ($offsetType === null) {
71: return new ErrorType();
72: }
73:
74: $valueStringType = $valueType->toString();
75: if ($valueStringType instanceof ErrorType) {
76: return new ErrorType();
77: }
78:
79: if ((new IntegerType())->isSuperTypeOf($offsetType)->yes() || $offsetType instanceof MixedType) {
80: return new StringType();
81: }
82:
83: return new ErrorType();
84: }
85:
86: public function unsetOffset(Type $offsetType): Type
87: {
88: return new ErrorType();
89: }
90:
91: public function accepts(Type $type, bool $strictTypes): TrinaryLogic
92: {
93: if ($type instanceof self) {
94: return TrinaryLogic::createYes();
95: }
96:
97: if ($type instanceof CompoundType) {
98: return $type->isAcceptedBy($this, $strictTypes);
99: }
100:
101: if ($type instanceof TypeWithClassName && !$strictTypes) {
102: $reflectionProvider = ReflectionProviderStaticAccessor::getInstance();
103: if (!$reflectionProvider->hasClass($type->getClassName())) {
104: return TrinaryLogic::createNo();
105: }
106:
107: $typeClass = $reflectionProvider->getClass($type->getClassName());
108: return TrinaryLogic::createFromBoolean(
109: $typeClass->hasNativeMethod('__toString'),
110: );
111: }
112:
113: return TrinaryLogic::createNo();
114: }
115:
116: public function toNumber(): Type
117: {
118: return new ErrorType();
119: }
120:
121: public function toInteger(): Type
122: {
123: return new IntegerType();
124: }
125:
126: public function toFloat(): Type
127: {
128: return new FloatType();
129: }
130:
131: public function toString(): Type
132: {
133: return $this;
134: }
135:
136: public function toArray(): Type
137: {
138: return new ConstantArrayType(
139: [new ConstantIntegerType(0)],
140: [$this],
141: [1],
142: [],
143: true,
144: );
145: }
146:
147: public function toArrayKey(): Type
148: {
149: return $this;
150: }
151:
152: public function isNull(): TrinaryLogic
153: {
154: return TrinaryLogic::createNo();
155: }
156:
157: public function isTrue(): TrinaryLogic
158: {
159: return TrinaryLogic::createNo();
160: }
161:
162: public function isFalse(): TrinaryLogic
163: {
164: return TrinaryLogic::createNo();
165: }
166:
167: public function isBoolean(): TrinaryLogic
168: {
169: return TrinaryLogic::createNo();
170: }
171:
172: public function isFloat(): TrinaryLogic
173: {
174: return TrinaryLogic::createNo();
175: }
176:
177: public function isInteger(): TrinaryLogic
178: {
179: return TrinaryLogic::createNo();
180: }
181:
182: public function isString(): TrinaryLogic
183: {
184: return TrinaryLogic::createYes();
185: }
186:
187: public function isNumericString(): TrinaryLogic
188: {
189: return TrinaryLogic::createMaybe();
190: }
191:
192: public function isNonEmptyString(): TrinaryLogic
193: {
194: return TrinaryLogic::createMaybe();
195: }
196:
197: public function isNonFalsyString(): TrinaryLogic
198: {
199: return TrinaryLogic::createMaybe();
200: }
201:
202: public function isLiteralString(): TrinaryLogic
203: {
204: return TrinaryLogic::createMaybe();
205: }
206:
207: public function isClassStringType(): TrinaryLogic
208: {
209: return TrinaryLogic::createMaybe();
210: }
211:
212: public function isScalar(): TrinaryLogic
213: {
214: return TrinaryLogic::createYes();
215: }
216:
217: public function hasMethod(string $methodName): TrinaryLogic
218: {
219: if ($this->isClassStringType()->yes()) {
220: return TrinaryLogic::createMaybe();
221: }
222: return TrinaryLogic::createNo();
223: }
224:
225: public function tryRemove(Type $typeToRemove): ?Type
226: {
227: if ($typeToRemove instanceof ConstantStringType && $typeToRemove->getValue() === '') {
228: return TypeCombinator::intersect($this, new AccessoryNonEmptyStringType());
229: }
230:
231: if ($typeToRemove instanceof AccessoryNonEmptyStringType) {
232: return new ConstantStringType('');
233: }
234:
235: return null;
236: }
237:
238: /**
239: * @param mixed[] $properties
240: */
241: public static function __set_state(array $properties): Type
242: {
243: return new self();
244: }
245:
246: }
247: