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