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