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