1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type\Accessory;
4:
5: use PHPStan\Php\PhpVersion;
6: use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
7: use PHPStan\PhpDocParser\Ast\Type\TypeNode;
8: use PHPStan\TrinaryLogic;
9: use PHPStan\Type\AcceptsResult;
10: use PHPStan\Type\BenevolentUnionType;
11: use PHPStan\Type\BooleanType;
12: use PHPStan\Type\CompoundType;
13: use PHPStan\Type\Constant\ConstantArrayType;
14: use PHPStan\Type\Constant\ConstantBooleanType;
15: use PHPStan\Type\Constant\ConstantIntegerType;
16: use PHPStan\Type\Constant\ConstantStringType;
17: use PHPStan\Type\ErrorType;
18: use PHPStan\Type\FloatType;
19: use PHPStan\Type\GeneralizePrecision;
20: use PHPStan\Type\IntegerType;
21: use PHPStan\Type\IntersectionType;
22: use PHPStan\Type\IsSuperTypeOfResult;
23: use PHPStan\Type\ObjectWithoutClassType;
24: use PHPStan\Type\StringType;
25: use PHPStan\Type\Traits\MaybeCallableTypeTrait;
26: use PHPStan\Type\Traits\NonArrayTypeTrait;
27: use PHPStan\Type\Traits\NonGenericTypeTrait;
28: use PHPStan\Type\Traits\NonIterableTypeTrait;
29: use PHPStan\Type\Traits\NonObjectTypeTrait;
30: use PHPStan\Type\Traits\UndecidedBooleanTypeTrait;
31: use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
32: use PHPStan\Type\Type;
33: use PHPStan\Type\TypeCombinator;
34: use PHPStan\Type\UnionType;
35: use PHPStan\Type\VerbosityLevel;
36:
37: class AccessoryNonEmptyStringType implements CompoundType, AccessoryType
38: {
39:
40: use MaybeCallableTypeTrait;
41: use NonArrayTypeTrait;
42: use NonObjectTypeTrait;
43: use NonIterableTypeTrait;
44: use UndecidedComparisonCompoundTypeTrait;
45: use NonGenericTypeTrait;
46: use UndecidedBooleanTypeTrait;
47:
48: /** @api */
49: public function __construct()
50: {
51: }
52:
53: public function getReferencedClasses(): array
54: {
55: return [];
56: }
57:
58: public function getObjectClassNames(): array
59: {
60: return [];
61: }
62:
63: public function getObjectClassReflections(): array
64: {
65: return [];
66: }
67:
68: public function getConstantStrings(): array
69: {
70: return [];
71: }
72:
73: public function accepts(Type $type, bool $strictTypes): AcceptsResult
74: {
75: if ($type instanceof CompoundType) {
76: return $type->isAcceptedBy($this, $strictTypes);
77: }
78:
79: return new AcceptsResult($type->isNonEmptyString(), []);
80: }
81:
82: public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
83: {
84: if ($type instanceof CompoundType) {
85: return $type->isSubTypeOf($this);
86: }
87:
88: if ($this->equals($type)) {
89: return IsSuperTypeOfResult::createYes();
90: }
91:
92: if ($type->isNonFalsyString()->yes()) {
93: return IsSuperTypeOfResult::createYes();
94: }
95:
96: return new IsSuperTypeOfResult($type->isNonEmptyString(), []);
97: }
98:
99: public function isSubTypeOf(Type $otherType): IsSuperTypeOfResult
100: {
101: if ($otherType instanceof UnionType || $otherType instanceof IntersectionType) {
102: return $otherType->isSuperTypeOf($this);
103: }
104:
105: return (new IsSuperTypeOfResult($otherType->isNonEmptyString(), []))
106: ->and($otherType instanceof self ? IsSuperTypeOfResult::createYes() : IsSuperTypeOfResult::createMaybe());
107: }
108:
109: public function isAcceptedBy(Type $acceptingType, bool $strictTypes): AcceptsResult
110: {
111: return $this->isSubTypeOf($acceptingType)->toAcceptsResult();
112: }
113:
114: public function equals(Type $type): bool
115: {
116: return $type instanceof self;
117: }
118:
119: public function describe(VerbosityLevel $level): string
120: {
121: return 'non-empty-string';
122: }
123:
124: public function isOffsetAccessible(): TrinaryLogic
125: {
126: return TrinaryLogic::createYes();
127: }
128:
129: public function isOffsetAccessLegal(): TrinaryLogic
130: {
131: return TrinaryLogic::createYes();
132: }
133:
134: public function hasOffsetValueType(Type $offsetType): TrinaryLogic
135: {
136: return $offsetType->isInteger()->and(TrinaryLogic::createMaybe());
137: }
138:
139: public function getOffsetValueType(Type $offsetType): Type
140: {
141: if ($this->hasOffsetValueType($offsetType)->no()) {
142: return new ErrorType();
143: }
144:
145: if ((new ConstantIntegerType(0))->isSuperTypeOf($offsetType)->yes()) {
146: return new IntersectionType([new StringType(), new AccessoryNonEmptyStringType()]);
147: }
148:
149: return new StringType();
150: }
151:
152: public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
153: {
154: $stringOffset = (new StringType())->setOffsetValueType($offsetType, $valueType, $unionValues);
155:
156: if ($stringOffset instanceof ErrorType) {
157: return $stringOffset;
158: }
159:
160: return $this;
161: }
162:
163: public function setExistingOffsetValueType(Type $offsetType, Type $valueType): Type
164: {
165: return $this;
166: }
167:
168: public function unsetOffset(Type $offsetType): Type
169: {
170: return new ErrorType();
171: }
172:
173: public function toNumber(): Type
174: {
175: return new ErrorType();
176: }
177:
178: public function toAbsoluteNumber(): Type
179: {
180: return new ErrorType();
181: }
182:
183: public function toInteger(): Type
184: {
185: return new IntegerType();
186: }
187:
188: public function toFloat(): Type
189: {
190: return new FloatType();
191: }
192:
193: public function toString(): Type
194: {
195: return $this;
196: }
197:
198: public function toArray(): Type
199: {
200: return new ConstantArrayType(
201: [new ConstantIntegerType(0)],
202: [$this],
203: [1],
204: [],
205: TrinaryLogic::createYes(),
206: );
207: }
208:
209: public function toArrayKey(): Type
210: {
211: return $this;
212: }
213:
214: public function isNull(): TrinaryLogic
215: {
216: return TrinaryLogic::createNo();
217: }
218:
219: public function isConstantValue(): TrinaryLogic
220: {
221: return TrinaryLogic::createMaybe();
222: }
223:
224: public function isConstantScalarValue(): TrinaryLogic
225: {
226: return TrinaryLogic::createMaybe();
227: }
228:
229: public function getConstantScalarTypes(): array
230: {
231: return [];
232: }
233:
234: public function getConstantScalarValues(): array
235: {
236: return [];
237: }
238:
239: public function isTrue(): TrinaryLogic
240: {
241: return TrinaryLogic::createNo();
242: }
243:
244: public function isFalse(): TrinaryLogic
245: {
246: return TrinaryLogic::createNo();
247: }
248:
249: public function isBoolean(): TrinaryLogic
250: {
251: return TrinaryLogic::createNo();
252: }
253:
254: public function isFloat(): TrinaryLogic
255: {
256: return TrinaryLogic::createNo();
257: }
258:
259: public function isInteger(): TrinaryLogic
260: {
261: return TrinaryLogic::createNo();
262: }
263:
264: public function isString(): TrinaryLogic
265: {
266: return TrinaryLogic::createYes();
267: }
268:
269: public function isNumericString(): TrinaryLogic
270: {
271: return TrinaryLogic::createMaybe();
272: }
273:
274: public function isNonEmptyString(): TrinaryLogic
275: {
276: return TrinaryLogic::createYes();
277: }
278:
279: public function isNonFalsyString(): TrinaryLogic
280: {
281: return TrinaryLogic::createMaybe();
282: }
283:
284: public function isLiteralString(): TrinaryLogic
285: {
286: return TrinaryLogic::createMaybe();
287: }
288:
289: public function isLowercaseString(): TrinaryLogic
290: {
291: return TrinaryLogic::createMaybe();
292: }
293:
294: public function isClassString(): TrinaryLogic
295: {
296: return TrinaryLogic::createMaybe();
297: }
298:
299: public function isUppercaseString(): TrinaryLogic
300: {
301: return TrinaryLogic::createMaybe();
302: }
303:
304: public function getClassStringObjectType(): Type
305: {
306: return new ObjectWithoutClassType();
307: }
308:
309: public function getObjectTypeOrClassStringObjectType(): Type
310: {
311: return new ObjectWithoutClassType();
312: }
313:
314: public function isVoid(): TrinaryLogic
315: {
316: return TrinaryLogic::createNo();
317: }
318:
319: public function isScalar(): TrinaryLogic
320: {
321: return TrinaryLogic::createYes();
322: }
323:
324: public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
325: {
326: if ($type->isNull()->yes()) {
327: return new ConstantBooleanType(false);
328: }
329:
330: if ($type->isString()->yes() && $type->isNonEmptyString()->no()) {
331: return new ConstantBooleanType(false);
332: }
333:
334: return new BooleanType();
335: }
336:
337: public function traverse(callable $cb): Type
338: {
339: return $this;
340: }
341:
342: public function traverseSimultaneously(Type $right, callable $cb): Type
343: {
344: return $this;
345: }
346:
347: public function generalize(GeneralizePrecision $precision): Type
348: {
349: return new StringType();
350: }
351:
352: public function tryRemove(Type $typeToRemove): ?Type
353: {
354: if ($typeToRemove instanceof ConstantStringType && $typeToRemove->getValue() === '0') {
355: return TypeCombinator::intersect($this, new AccessoryNonFalsyStringType());
356: }
357:
358: return null;
359: }
360:
361: public function exponentiate(Type $exponent): Type
362: {
363: return new BenevolentUnionType([
364: new FloatType(),
365: new IntegerType(),
366: ]);
367: }
368:
369: public function getFiniteTypes(): array
370: {
371: return [];
372: }
373:
374: public function toPhpDocNode(): TypeNode
375: {
376: return new IdentifierTypeNode('non-empty-string');
377: }
378:
379: }
380: