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 isOffsetAccessLegal(): TrinaryLogic
60: {
61: return TrinaryLogic::createYes();
62: }
63:
64: public function hasOffsetValueType(Type $offsetType): TrinaryLogic
65: {
66: return $offsetType->isInteger()->and(TrinaryLogic::createMaybe());
67: }
68:
69: public function getOffsetValueType(Type $offsetType): Type
70: {
71: if ($this->hasOffsetValueType($offsetType)->no()) {
72: return new ErrorType();
73: }
74:
75: return new StringType();
76: }
77:
78: public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
79: {
80: if ($offsetType === null) {
81: return new ErrorType();
82: }
83:
84: $valueStringType = $valueType->toString();
85: if ($valueStringType instanceof ErrorType) {
86: return new ErrorType();
87: }
88:
89: if ($offsetType->isInteger()->yes() || $offsetType instanceof MixedType) {
90: return new StringType();
91: }
92:
93: return new ErrorType();
94: }
95:
96: public function setExistingOffsetValueType(Type $offsetType, Type $valueType): Type
97: {
98: return $this;
99: }
100:
101: public function unsetOffset(Type $offsetType): Type
102: {
103: return new ErrorType();
104: }
105:
106: public function accepts(Type $type, bool $strictTypes): TrinaryLogic
107: {
108: return $this->acceptsWithReason($type, $strictTypes)->result;
109: }
110:
111: public function acceptsWithReason(Type $type, bool $strictTypes): AcceptsResult
112: {
113: if ($type instanceof self) {
114: return AcceptsResult::createYes();
115: }
116:
117: if ($type instanceof CompoundType) {
118: return $type->isAcceptedWithReasonBy($this, $strictTypes);
119: }
120:
121: $thatClassNames = $type->getObjectClassNames();
122: if (count($thatClassNames) > 1) {
123: throw new ShouldNotHappenException();
124: }
125:
126: if ($thatClassNames === [] || $strictTypes) {
127: return AcceptsResult::createNo();
128: }
129:
130: $reflectionProvider = ReflectionProviderStaticAccessor::getInstance();
131: if (!$reflectionProvider->hasClass($thatClassNames[0])) {
132: return AcceptsResult::createNo();
133: }
134:
135: $typeClass = $reflectionProvider->getClass($thatClassNames[0]);
136: return AcceptsResult::createFromBoolean(
137: $typeClass->hasNativeMethod('__toString'),
138: );
139: }
140:
141: public function toNumber(): Type
142: {
143: return new ErrorType();
144: }
145:
146: public function toInteger(): Type
147: {
148: return new IntegerType();
149: }
150:
151: public function toFloat(): Type
152: {
153: return new FloatType();
154: }
155:
156: public function toString(): Type
157: {
158: return $this;
159: }
160:
161: public function toArray(): Type
162: {
163: return new ConstantArrayType(
164: [new ConstantIntegerType(0)],
165: [$this],
166: [1],
167: [],
168: TrinaryLogic::createYes(),
169: );
170: }
171:
172: public function toArrayKey(): Type
173: {
174: return $this;
175: }
176:
177: public function isNull(): TrinaryLogic
178: {
179: return TrinaryLogic::createNo();
180: }
181:
182: public function isTrue(): TrinaryLogic
183: {
184: return TrinaryLogic::createNo();
185: }
186:
187: public function isFalse(): TrinaryLogic
188: {
189: return TrinaryLogic::createNo();
190: }
191:
192: public function isBoolean(): TrinaryLogic
193: {
194: return TrinaryLogic::createNo();
195: }
196:
197: public function isFloat(): TrinaryLogic
198: {
199: return TrinaryLogic::createNo();
200: }
201:
202: public function isInteger(): TrinaryLogic
203: {
204: return TrinaryLogic::createNo();
205: }
206:
207: public function isString(): TrinaryLogic
208: {
209: return TrinaryLogic::createYes();
210: }
211:
212: public function isNumericString(): TrinaryLogic
213: {
214: return TrinaryLogic::createMaybe();
215: }
216:
217: public function isNonEmptyString(): TrinaryLogic
218: {
219: return TrinaryLogic::createMaybe();
220: }
221:
222: public function isNonFalsyString(): TrinaryLogic
223: {
224: return TrinaryLogic::createMaybe();
225: }
226:
227: public function isLiteralString(): TrinaryLogic
228: {
229: return TrinaryLogic::createMaybe();
230: }
231:
232: public function isClassStringType(): TrinaryLogic
233: {
234: return TrinaryLogic::createMaybe();
235: }
236:
237: public function getClassStringObjectType(): Type
238: {
239: return new ObjectWithoutClassType();
240: }
241:
242: public function getObjectTypeOrClassStringObjectType(): Type
243: {
244: return new ObjectWithoutClassType();
245: }
246:
247: public function isScalar(): TrinaryLogic
248: {
249: return TrinaryLogic::createYes();
250: }
251:
252: public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
253: {
254: return new BooleanType();
255: }
256:
257: public function hasMethod(string $methodName): TrinaryLogic
258: {
259: if ($this->isClassStringType()->yes()) {
260: return TrinaryLogic::createMaybe();
261: }
262: return TrinaryLogic::createNo();
263: }
264:
265: public function tryRemove(Type $typeToRemove): ?Type
266: {
267: if ($typeToRemove instanceof ConstantStringType && $typeToRemove->getValue() === '') {
268: return TypeCombinator::intersect($this, new AccessoryNonEmptyStringType());
269: }
270:
271: if ($typeToRemove instanceof AccessoryNonEmptyStringType) {
272: return new ConstantStringType('');
273: }
274:
275: return null;
276: }
277:
278: public function getFiniteTypes(): array
279: {
280: return [];
281: }
282:
283: public function exponentiate(Type $exponent): Type
284: {
285: return ExponentiateHelper::exponentiate($this, $exponent);
286: }
287:
288: public function toPhpDocNode(): TypeNode
289: {
290: return new IdentifierTypeNode('string');
291: }
292:
293: /**
294: * @param mixed[] $properties
295: */
296: public static function __set_state(array $properties): Type
297: {
298: return new self();
299: }
300:
301: }
302: