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