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\NonGeneralizableTypeTrait;
13: use PHPStan\Type\Traits\NonGenericTypeTrait;
14: use PHPStan\Type\Traits\NonIterableTypeTrait;
15: use PHPStan\Type\Traits\NonObjectTypeTrait;
16: use PHPStan\Type\Traits\UndecidedBooleanTypeTrait;
17: use PHPStan\Type\Traits\UndecidedComparisonTypeTrait;
18:
19: /** @api */
20: class StringType implements Type
21: {
22:
23: use JustNullableTypeTrait;
24: use MaybeCallableTypeTrait;
25: use NonIterableTypeTrait;
26: use NonObjectTypeTrait;
27: use UndecidedBooleanTypeTrait;
28: use UndecidedComparisonTypeTrait;
29: use NonGenericTypeTrait;
30: use NonGeneralizableTypeTrait;
31:
32: /** @api */
33: public function __construct()
34: {
35: }
36:
37: public function describe(VerbosityLevel $level): string
38: {
39: return 'string';
40: }
41:
42: public function isOffsetAccessible(): TrinaryLogic
43: {
44: return TrinaryLogic::createYes();
45: }
46:
47: public function hasOffsetValueType(Type $offsetType): TrinaryLogic
48: {
49: return (new IntegerType())->isSuperTypeOf($offsetType)->and(TrinaryLogic::createMaybe());
50: }
51:
52: public function getOffsetValueType(Type $offsetType): Type
53: {
54: if ($this->hasOffsetValueType($offsetType)->no()) {
55: return new ErrorType();
56: }
57:
58: return new StringType();
59: }
60:
61: public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
62: {
63: if ($offsetType === null) {
64: return new ErrorType();
65: }
66:
67: $valueStringType = $valueType->toString();
68: if ($valueStringType instanceof ErrorType) {
69: return new ErrorType();
70: }
71:
72: if ((new IntegerType())->isSuperTypeOf($offsetType)->yes() || $offsetType instanceof MixedType) {
73: return new StringType();
74: }
75:
76: return new ErrorType();
77: }
78:
79: public function unsetOffset(Type $offsetType): Type
80: {
81: return new ErrorType();
82: }
83:
84: public function accepts(Type $type, bool $strictTypes): TrinaryLogic
85: {
86: if ($type instanceof self) {
87: return TrinaryLogic::createYes();
88: }
89:
90: if ($type instanceof CompoundType) {
91: return $type->isAcceptedBy($this, $strictTypes);
92: }
93:
94: if ($type instanceof TypeWithClassName && !$strictTypes) {
95: $reflectionProvider = ReflectionProviderStaticAccessor::getInstance();
96: if (!$reflectionProvider->hasClass($type->getClassName())) {
97: return TrinaryLogic::createNo();
98: }
99:
100: $typeClass = $reflectionProvider->getClass($type->getClassName());
101: return TrinaryLogic::createFromBoolean(
102: $typeClass->hasNativeMethod('__toString'),
103: );
104: }
105:
106: return TrinaryLogic::createNo();
107: }
108:
109: public function toNumber(): Type
110: {
111: return new ErrorType();
112: }
113:
114: public function toInteger(): Type
115: {
116: return new IntegerType();
117: }
118:
119: public function toFloat(): Type
120: {
121: return new FloatType();
122: }
123:
124: public function toString(): Type
125: {
126: return $this;
127: }
128:
129: public function toArray(): Type
130: {
131: return new ConstantArrayType(
132: [new ConstantIntegerType(0)],
133: [$this],
134: [1],
135: );
136: }
137:
138: public function isString(): TrinaryLogic
139: {
140: return TrinaryLogic::createYes();
141: }
142:
143: public function isNumericString(): TrinaryLogic
144: {
145: return TrinaryLogic::createMaybe();
146: }
147:
148: public function isNonEmptyString(): TrinaryLogic
149: {
150: return TrinaryLogic::createMaybe();
151: }
152:
153: public function isNonFalsyString(): TrinaryLogic
154: {
155: return TrinaryLogic::createMaybe();
156: }
157:
158: public function isLiteralString(): TrinaryLogic
159: {
160: return TrinaryLogic::createMaybe();
161: }
162:
163: public function tryRemove(Type $typeToRemove): ?Type
164: {
165: if ($typeToRemove instanceof ConstantStringType && $typeToRemove->getValue() === '') {
166: return TypeCombinator::intersect($this, new AccessoryNonEmptyStringType());
167: }
168:
169: if ($typeToRemove instanceof AccessoryNonEmptyStringType) {
170: return new ConstantStringType('');
171: }
172:
173: return null;
174: }
175:
176: /**
177: * @param mixed[] $properties
178: */
179: public static function __set_state(array $properties): Type
180: {
181: return new self();
182: }
183:
184: }
185: