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