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\ArrayType;
11: use PHPStan\Type\BooleanType;
12: use PHPStan\Type\CompoundType;
13: use PHPStan\Type\Constant\ConstantFloatType;
14: use PHPStan\Type\Constant\ConstantIntegerType;
15: use PHPStan\Type\ErrorType;
16: use PHPStan\Type\IntegerRangeType;
17: use PHPStan\Type\IntersectionType;
18: use PHPStan\Type\IsSuperTypeOfResult;
19: use PHPStan\Type\MixedType;
20: use PHPStan\Type\Traits\MaybeCallableTypeTrait;
21: use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
22: use PHPStan\Type\Traits\NonGenericTypeTrait;
23: use PHPStan\Type\Traits\NonObjectTypeTrait;
24: use PHPStan\Type\Traits\NonRemoveableTypeTrait;
25: use PHPStan\Type\Traits\UndecidedBooleanTypeTrait;
26: use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
27: use PHPStan\Type\Type;
28: use PHPStan\Type\UnionType;
29: use PHPStan\Type\VerbosityLevel;
30:
31: /** @api */
32: class AccessoryArrayListType implements CompoundType, AccessoryType
33: {
34:
35: use MaybeCallableTypeTrait;
36: use NonObjectTypeTrait;
37: use NonGenericTypeTrait;
38: use UndecidedBooleanTypeTrait;
39: use UndecidedComparisonCompoundTypeTrait;
40: use NonRemoveableTypeTrait;
41: use NonGeneralizableTypeTrait;
42:
43: /** @api */
44: public function __construct()
45: {
46: }
47:
48: public function getReferencedClasses(): array
49: {
50: return [];
51: }
52:
53: public function getObjectClassNames(): array
54: {
55: return [];
56: }
57:
58: public function getObjectClassReflections(): array
59: {
60: return [];
61: }
62:
63: public function getArrays(): array
64: {
65: return [];
66: }
67:
68: public function getConstantArrays(): array
69: {
70: return [];
71: }
72:
73: public function getConstantStrings(): array
74: {
75: return [];
76: }
77:
78: public function accepts(Type $type, bool $strictTypes): AcceptsResult
79: {
80: $isArray = $type->isArray();
81: $isList = $type->isList();
82: $isListArray = $isArray->and($isList);
83:
84: if ($isListArray->yes()) {
85: return AcceptsResult::createYes();
86: }
87:
88: if ($type instanceof CompoundType) {
89: return $type->isAcceptedBy($this, $strictTypes);
90: }
91:
92: return new AcceptsResult($isListArray, []);
93: }
94:
95: public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
96: {
97: if ($this->equals($type)) {
98: return IsSuperTypeOfResult::createYes();
99: }
100:
101: if ($type instanceof CompoundType) {
102: return $type->isSubTypeOf($this);
103: }
104:
105: return new IsSuperTypeOfResult($type->isArray()->and($type->isList()), []);
106: }
107:
108: public function isSubTypeOf(Type $otherType): IsSuperTypeOfResult
109: {
110: if ($otherType instanceof UnionType || $otherType instanceof IntersectionType) {
111: return $otherType->isSuperTypeOf($this);
112: }
113:
114: return new IsSuperTypeOfResult(
115: $otherType->isArray()->and($otherType->isList())->and($otherType instanceof self ? TrinaryLogic::createYes() : TrinaryLogic::createMaybe()),
116: [],
117: );
118: }
119:
120: public function isAcceptedBy(Type $acceptingType, bool $strictTypes): AcceptsResult
121: {
122: return $this->isSubTypeOf($acceptingType)->toAcceptsResult();
123: }
124:
125: public function equals(Type $type): bool
126: {
127: return $type instanceof self;
128: }
129:
130: public function describe(VerbosityLevel $level): string
131: {
132: return 'list';
133: }
134:
135: public function isOffsetAccessible(): TrinaryLogic
136: {
137: return TrinaryLogic::createYes();
138: }
139:
140: public function isOffsetAccessLegal(): TrinaryLogic
141: {
142: return TrinaryLogic::createYes();
143: }
144:
145: public function hasOffsetValueType(Type $offsetType): TrinaryLogic
146: {
147: return $this->getIterableKeyType()->isSuperTypeOf($offsetType)->result->and(TrinaryLogic::createMaybe());
148: }
149:
150: public function getOffsetValueType(Type $offsetType): Type
151: {
152: return new MixedType();
153: }
154:
155: public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
156: {
157: if ($offsetType === null || (new ConstantIntegerType(0))->isSuperTypeOf($offsetType)->yes()) {
158: return $this;
159: }
160:
161: return new ErrorType();
162: }
163:
164: public function setExistingOffsetValueType(Type $offsetType, Type $valueType): Type
165: {
166: return $this;
167: }
168:
169: public function unsetOffset(Type $offsetType): Type
170: {
171: if ($this->hasOffsetValueType($offsetType)->no()) {
172: return $this;
173: }
174:
175: return new ErrorType();
176: }
177:
178: public function getKeysArrayFiltered(Type $filterValueType, TrinaryLogic $strict): Type
179: {
180: return $this->getKeysArray();
181: }
182:
183: public function getKeysArray(): Type
184: {
185: return $this;
186: }
187:
188: public function getValuesArray(): Type
189: {
190: return $this;
191: }
192:
193: public function chunkArray(Type $lengthType, TrinaryLogic $preserveKeys): Type
194: {
195: return $this;
196: }
197:
198: public function fillKeysArray(Type $valueType): Type
199: {
200: return new MixedType();
201: }
202:
203: public function flipArray(): Type
204: {
205: return new MixedType();
206: }
207:
208: public function intersectKeyArray(Type $otherArraysType): Type
209: {
210: if ($otherArraysType->isList()->yes()) {
211: return $this;
212: }
213:
214: return new MixedType();
215: }
216:
217: public function popArray(): Type
218: {
219: return $this;
220: }
221:
222: public function reverseArray(TrinaryLogic $preserveKeys): Type
223: {
224: if ($preserveKeys->no()) {
225: return $this;
226: }
227:
228: return new MixedType();
229: }
230:
231: public function searchArray(Type $needleType, ?TrinaryLogic $strict = null): Type
232: {
233: return new MixedType();
234: }
235:
236: public function shiftArray(): Type
237: {
238: return $this;
239: }
240:
241: public function shuffleArray(): Type
242: {
243: return $this;
244: }
245:
246: public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
247: {
248: if ($preserveKeys->no()) {
249: return $this;
250: }
251:
252: if ((new ConstantIntegerType(0))->isSuperTypeOf($offsetType)->yes()) {
253: return $this;
254: }
255:
256: return new MixedType();
257: }
258:
259: public function spliceArray(Type $offsetType, Type $lengthType, Type $replacementType): Type
260: {
261: return $this;
262: }
263:
264: public function truncateListToSize(Type $sizeType): Type
265: {
266: // List-ness survives a count narrowing — the resulting array is
267: // still a list, just of a constrained size.
268: return $this;
269: }
270:
271: public function makeListMaybe(): Type
272: {
273: // This accessory is the list assertion itself; weakening the
274: // list-ness to "maybe" means the accessory no longer applies.
275: // Returning `MixedType` lets the enclosing `IntersectionType` drop
276: // it via `TypeCombinator::intersect` while preserving the rest.
277: return new MixedType();
278: }
279:
280: public function mapValueType(callable $cb): Type
281: {
282: // Mapping values doesn't disturb list-ness.
283: return $this;
284: }
285:
286: public function mapKeyType(callable $cb): Type
287: {
288: return $this;
289: }
290:
291: public function makeAllArrayKeysOptional(): Type
292: {
293: // Marking keys optional in an arbitrary list keeps it a list.
294: return $this;
295: }
296:
297: public function changeKeyCaseArray(?int $case): Type
298: {
299: // List keys are integers; case-folding leaves them alone.
300: return $this;
301: }
302:
303: public function filterArrayRemovingFalsey(): Type
304: {
305: // Filtering creates gaps in the integer-key sequence — list-ness lost.
306: return new MixedType();
307: }
308:
309: public function isIterable(): TrinaryLogic
310: {
311: return TrinaryLogic::createYes();
312: }
313:
314: public function isIterableAtLeastOnce(): TrinaryLogic
315: {
316: return TrinaryLogic::createMaybe();
317: }
318:
319: public function getArraySize(): Type
320: {
321: return IntegerRangeType::fromInterval(0, null);
322: }
323:
324: public function getIterableKeyType(): Type
325: {
326: return IntegerRangeType::fromInterval(0, null);
327: }
328:
329: public function getFirstIterableKeyType(): Type
330: {
331: return new ConstantIntegerType(0);
332: }
333:
334: public function getLastIterableKeyType(): Type
335: {
336: return $this->getIterableKeyType();
337: }
338:
339: public function getIterableValueType(): Type
340: {
341: return new MixedType();
342: }
343:
344: public function getFirstIterableValueType(): Type
345: {
346: return new MixedType();
347: }
348:
349: public function getLastIterableValueType(): Type
350: {
351: return new MixedType();
352: }
353:
354: public function isArray(): TrinaryLogic
355: {
356: return TrinaryLogic::createYes();
357: }
358:
359: public function isConstantArray(): TrinaryLogic
360: {
361: return TrinaryLogic::createMaybe();
362: }
363:
364: public function isOversizedArray(): TrinaryLogic
365: {
366: return TrinaryLogic::createMaybe();
367: }
368:
369: public function isList(): TrinaryLogic
370: {
371: return TrinaryLogic::createYes();
372: }
373:
374: public function isNull(): TrinaryLogic
375: {
376: return TrinaryLogic::createNo();
377: }
378:
379: public function isConstantValue(): TrinaryLogic
380: {
381: return TrinaryLogic::createMaybe();
382: }
383:
384: public function isConstantScalarValue(): TrinaryLogic
385: {
386: return TrinaryLogic::createNo();
387: }
388:
389: public function getConstantScalarTypes(): array
390: {
391: return [];
392: }
393:
394: public function getConstantScalarValues(): array
395: {
396: return [];
397: }
398:
399: public function isTrue(): TrinaryLogic
400: {
401: return TrinaryLogic::createNo();
402: }
403:
404: public function isFalse(): TrinaryLogic
405: {
406: return TrinaryLogic::createNo();
407: }
408:
409: public function isBoolean(): TrinaryLogic
410: {
411: return TrinaryLogic::createNo();
412: }
413:
414: public function isFloat(): TrinaryLogic
415: {
416: return TrinaryLogic::createNo();
417: }
418:
419: public function isInteger(): TrinaryLogic
420: {
421: return TrinaryLogic::createNo();
422: }
423:
424: public function isString(): TrinaryLogic
425: {
426: return TrinaryLogic::createNo();
427: }
428:
429: public function isNumericString(): TrinaryLogic
430: {
431: return TrinaryLogic::createNo();
432: }
433:
434: public function isDecimalIntegerString(): TrinaryLogic
435: {
436: return TrinaryLogic::createNo();
437: }
438:
439: public function isNonEmptyString(): TrinaryLogic
440: {
441: return TrinaryLogic::createNo();
442: }
443:
444: public function isNonFalsyString(): TrinaryLogic
445: {
446: return TrinaryLogic::createNo();
447: }
448:
449: public function isLiteralString(): TrinaryLogic
450: {
451: return TrinaryLogic::createNo();
452: }
453:
454: public function isLowercaseString(): TrinaryLogic
455: {
456: return TrinaryLogic::createNo();
457: }
458:
459: public function isClassString(): TrinaryLogic
460: {
461: return TrinaryLogic::createNo();
462: }
463:
464: public function isUppercaseString(): TrinaryLogic
465: {
466: return TrinaryLogic::createNo();
467: }
468:
469: public function getClassStringObjectType(): Type
470: {
471: return new ErrorType();
472: }
473:
474: public function getObjectTypeOrClassStringObjectType(): Type
475: {
476: return new ErrorType();
477: }
478:
479: public function isVoid(): TrinaryLogic
480: {
481: return TrinaryLogic::createNo();
482: }
483:
484: public function isScalar(): TrinaryLogic
485: {
486: return TrinaryLogic::createNo();
487: }
488:
489: public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
490: {
491: return new BooleanType();
492: }
493:
494: public function toNumber(): Type
495: {
496: return new ErrorType();
497: }
498:
499: public function toBitwiseNotType(): Type
500: {
501: return new ErrorType();
502: }
503:
504: public function toAbsoluteNumber(): Type
505: {
506: return new ErrorType();
507: }
508:
509: public function toInteger(): Type
510: {
511: return new UnionType([
512: new ConstantIntegerType(0),
513: new ConstantIntegerType(1),
514: ]);
515: }
516:
517: public function toFloat(): Type
518: {
519: return new UnionType([
520: new ConstantFloatType(0.0),
521: new ConstantFloatType(1.0),
522: ]);
523: }
524:
525: public function toString(): Type
526: {
527: return new ErrorType();
528: }
529:
530: public function toArray(): Type
531: {
532: return $this;
533: }
534:
535: public function toArrayKey(): Type
536: {
537: return new ErrorType();
538: }
539:
540: public function toCoercedArgumentType(bool $strictTypes): Type
541: {
542: return $this;
543: }
544:
545: public function traverse(callable $cb): Type
546: {
547: return $this;
548: }
549:
550: public function traverseSimultaneously(Type $right, callable $cb): Type
551: {
552: return $this;
553: }
554:
555: public function exponentiate(Type $exponent): Type
556: {
557: return new ErrorType();
558: }
559:
560: public function getFiniteTypes(): array
561: {
562: return [];
563: }
564:
565: public function getDefaultBaseType(): Type
566: {
567: return new ArrayType(new MixedType(), new MixedType());
568: }
569:
570: public function toPhpDocNode(): TypeNode
571: {
572: return new IdentifierTypeNode('list');
573: }
574:
575: public function hasTemplateOrLateResolvableType(): bool
576: {
577: return false;
578: }
579:
580: }
581: