1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type;
4:
5: use ArrayAccess;
6: use PHPStan\Php\PhpVersion;
7: use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
8: use PHPStan\PhpDocParser\Ast\Type\TypeNode;
9: use PHPStan\Reflection\ClassConstantReflection;
10: use PHPStan\Reflection\ClassMemberAccessAnswerer;
11: use PHPStan\Reflection\Dummy\DummyClassConstantReflection;
12: use PHPStan\Reflection\Dummy\DummyMethodReflection;
13: use PHPStan\Reflection\Dummy\DummyPropertyReflection;
14: use PHPStan\Reflection\ExtendedMethodReflection;
15: use PHPStan\Reflection\ExtendedPropertyReflection;
16: use PHPStan\Reflection\TrivialParametersAcceptor;
17: use PHPStan\Reflection\Type\CallbackUnresolvedMethodPrototypeReflection;
18: use PHPStan\Reflection\Type\CallbackUnresolvedPropertyPrototypeReflection;
19: use PHPStan\Reflection\Type\UnresolvedMethodPrototypeReflection;
20: use PHPStan\Reflection\Type\UnresolvedPropertyPrototypeReflection;
21: use PHPStan\TrinaryLogic;
22: use PHPStan\Type\Accessory\AccessoryArrayListType;
23: use PHPStan\Type\Accessory\AccessoryLiteralStringType;
24: use PHPStan\Type\Accessory\AccessoryLowercaseStringType;
25: use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
26: use PHPStan\Type\Accessory\AccessoryNonFalsyStringType;
27: use PHPStan\Type\Accessory\AccessoryNumericStringType;
28: use PHPStan\Type\Accessory\AccessoryUppercaseStringType;
29: use PHPStan\Type\Accessory\OversizedArrayType;
30: use PHPStan\Type\Constant\ConstantArrayType;
31: use PHPStan\Type\Constant\ConstantBooleanType;
32: use PHPStan\Type\Constant\ConstantFloatType;
33: use PHPStan\Type\Constant\ConstantIntegerType;
34: use PHPStan\Type\Constant\ConstantStringType;
35: use PHPStan\Type\Enum\EnumCaseObjectType;
36: use PHPStan\Type\Generic\TemplateMixedType;
37: use PHPStan\Type\Generic\TemplateType;
38: use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
39: use PHPStan\Type\Traits\NonGenericTypeTrait;
40: use PHPStan\Type\Traits\SubstractableTypeTrait;
41: use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
42: use function get_class;
43: use function sprintf;
44:
45: /** @api */
46: class MixedType implements CompoundType, SubtractableType
47: {
48:
49: use NonGenericTypeTrait;
50: use UndecidedComparisonCompoundTypeTrait;
51: use NonGeneralizableTypeTrait;
52: use SubstractableTypeTrait;
53:
54: private ?Type $subtractedType;
55:
56: /** @api */
57: public function __construct(
58: private bool $isExplicitMixed = false,
59: ?Type $subtractedType = null,
60: )
61: {
62: if ($subtractedType instanceof NeverType) {
63: $subtractedType = null;
64: }
65:
66: $this->subtractedType = $subtractedType;
67: }
68:
69: public function getReferencedClasses(): array
70: {
71: return [];
72: }
73:
74: public function getObjectClassNames(): array
75: {
76: return [];
77: }
78:
79: public function getObjectClassReflections(): array
80: {
81: return [];
82: }
83:
84: public function getArrays(): array
85: {
86: return [];
87: }
88:
89: public function getConstantArrays(): array
90: {
91: return [];
92: }
93:
94: public function getConstantStrings(): array
95: {
96: return [];
97: }
98:
99: public function accepts(Type $type, bool $strictTypes): AcceptsResult
100: {
101: return AcceptsResult::createYes();
102: }
103:
104: public function isSuperTypeOfMixed(MixedType $type): IsSuperTypeOfResult
105: {
106: if ($this->subtractedType === null) {
107: if ($this->isExplicitMixed) {
108: if ($type->isExplicitMixed) {
109: return IsSuperTypeOfResult::createYes();
110: }
111: return IsSuperTypeOfResult::createMaybe();
112: }
113:
114: return IsSuperTypeOfResult::createYes();
115: }
116:
117: if ($type->subtractedType === null) {
118: return IsSuperTypeOfResult::createMaybe();
119: }
120:
121: $isSuperType = $type->subtractedType->isSuperTypeOf($this->subtractedType);
122: if ($isSuperType->yes()) {
123: if ($this->isExplicitMixed) {
124: if ($type->isExplicitMixed) {
125: return IsSuperTypeOfResult::createYes();
126: }
127: return IsSuperTypeOfResult::createMaybe();
128: }
129:
130: return IsSuperTypeOfResult::createYes();
131: }
132:
133: return IsSuperTypeOfResult::createMaybe();
134: }
135:
136: public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
137: {
138: if ($this->subtractedType === null || $type instanceof NeverType) {
139: return IsSuperTypeOfResult::createYes();
140: }
141:
142: if ($type instanceof self) {
143: if ($type->subtractedType === null) {
144: return IsSuperTypeOfResult::createMaybe();
145: }
146: $isSuperType = $type->subtractedType->isSuperTypeOf($this->subtractedType);
147: if ($isSuperType->yes()) {
148: return $isSuperType;
149: }
150:
151: return IsSuperTypeOfResult::createMaybe();
152: }
153:
154: $result = $this->subtractedType->isSuperTypeOf($type)->negate();
155: if ($result->no()) {
156: return IsSuperTypeOfResult::createNo([
157: sprintf(
158: 'Type %s has already been eliminated from %s.',
159: $this->subtractedType->describe(VerbosityLevel::precise()),
160: $this->describe(VerbosityLevel::typeOnly()),
161: ),
162: ]);
163: }
164:
165: return $result;
166: }
167:
168: public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type
169: {
170: return new self($this->isExplicitMixed);
171: }
172:
173: public function setExistingOffsetValueType(Type $offsetType, Type $valueType): Type
174: {
175: return new self($this->isExplicitMixed);
176: }
177:
178: public function unsetOffset(Type $offsetType): Type
179: {
180: if ($this->subtractedType !== null) {
181: return new self($this->isExplicitMixed, TypeCombinator::remove($this->subtractedType, new ConstantArrayType([], [])));
182: }
183: return $this;
184: }
185:
186: public function getKeysArrayFiltered(Type $filterValueType, TrinaryLogic $strict): Type
187: {
188: return $this->getKeysArray();
189: }
190:
191: public function getKeysArray(): Type
192: {
193: if ($this->isArray()->no()) {
194: return new ErrorType();
195: }
196:
197: return new IntersectionType([new ArrayType(IntegerRangeType::createAllGreaterThanOrEqualTo(0), new UnionType([new IntegerType(), new StringType()])), new AccessoryArrayListType()]);
198: }
199:
200: public function getValuesArray(): Type
201: {
202: if ($this->isArray()->no()) {
203: return new ErrorType();
204: }
205:
206: return new IntersectionType([new ArrayType(IntegerRangeType::createAllGreaterThanOrEqualTo(0), new MixedType($this->isExplicitMixed)), new AccessoryArrayListType()]);
207: }
208:
209: public function chunkArray(Type $lengthType, TrinaryLogic $preserveKeys): Type
210: {
211: if ($this->isArray()->no()) {
212: return new ErrorType();
213: }
214:
215: return new IntersectionType([new ArrayType(IntegerRangeType::createAllGreaterThanOrEqualTo(0), new MixedType($this->isExplicitMixed)), new AccessoryArrayListType()]);
216: }
217:
218: public function fillKeysArray(Type $valueType): Type
219: {
220: if ($this->isArray()->no()) {
221: return new ErrorType();
222: }
223:
224: return new ArrayType($this->getIterableValueType(), $valueType);
225: }
226:
227: public function flipArray(): Type
228: {
229: if ($this->isArray()->no()) {
230: return new ErrorType();
231: }
232:
233: return new ArrayType(new MixedType($this->isExplicitMixed), new MixedType($this->isExplicitMixed));
234: }
235:
236: public function intersectKeyArray(Type $otherArraysType): Type
237: {
238: if ($this->isArray()->no()) {
239: return new ErrorType();
240: }
241:
242: return new ArrayType(new MixedType($this->isExplicitMixed), new MixedType($this->isExplicitMixed));
243: }
244:
245: public function popArray(): Type
246: {
247: if ($this->isArray()->no()) {
248: return new ErrorType();
249: }
250:
251: return new ArrayType(new MixedType($this->isExplicitMixed), new MixedType($this->isExplicitMixed));
252: }
253:
254: public function reverseArray(TrinaryLogic $preserveKeys): Type
255: {
256: if ($this->isArray()->no()) {
257: return new ErrorType();
258: }
259:
260: return new ArrayType(new MixedType($this->isExplicitMixed), new MixedType($this->isExplicitMixed));
261: }
262:
263: public function searchArray(Type $needleType, ?TrinaryLogic $strict = null): Type
264: {
265: if ($this->isArray()->no()) {
266: return new ErrorType();
267: }
268:
269: return new UnionType([new IntegerType(), new StringType(), new ConstantBooleanType(false)]);
270: }
271:
272: public function shiftArray(): Type
273: {
274: if ($this->isArray()->no()) {
275: return new ErrorType();
276: }
277:
278: return new ArrayType(new MixedType($this->isExplicitMixed), new MixedType($this->isExplicitMixed));
279: }
280:
281: public function shuffleArray(): Type
282: {
283: if ($this->isArray()->no()) {
284: return new ErrorType();
285: }
286:
287: return new IntersectionType([new ArrayType(IntegerRangeType::createAllGreaterThanOrEqualTo(0), new MixedType($this->isExplicitMixed)), new AccessoryArrayListType()]);
288: }
289:
290: public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
291: {
292: if ($this->isArray()->no()) {
293: return new ErrorType();
294: }
295:
296: return new ArrayType(new MixedType($this->isExplicitMixed), new MixedType($this->isExplicitMixed));
297: }
298:
299: public function spliceArray(Type $offsetType, Type $lengthType, Type $replacementType): Type
300: {
301: if ($this->isArray()->no()) {
302: return new ErrorType();
303: }
304:
305: return new ArrayType(new MixedType($this->isExplicitMixed), new MixedType($this->isExplicitMixed));
306: }
307:
308: public function isCallable(): TrinaryLogic
309: {
310: if ($this->subtractedType !== null) {
311: if ($this->subtractedType->isSuperTypeOf(new CallableType())->yes()) {
312: return TrinaryLogic::createNo();
313: }
314: }
315:
316: return TrinaryLogic::createMaybe();
317: }
318:
319: public function getEnumCases(): array
320: {
321: return [];
322: }
323:
324: public function getEnumCaseObject(): ?EnumCaseObjectType
325: {
326: return null;
327: }
328:
329: public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope): array
330: {
331: return [new TrivialParametersAcceptor()];
332: }
333:
334: public function equals(Type $type): bool
335: {
336: if (get_class($type) !== static::class) {
337: return false;
338: }
339:
340: if ($this->subtractedType === null) {
341: if ($type->subtractedType === null) {
342: return true;
343: }
344:
345: return false;
346: }
347:
348: if ($type->subtractedType === null) {
349: return false;
350: }
351:
352: return $this->subtractedType->equals($type->subtractedType);
353: }
354:
355: public function isSubTypeOf(Type $otherType): IsSuperTypeOfResult
356: {
357: if ($otherType instanceof self && !$otherType instanceof TemplateMixedType) {
358: return IsSuperTypeOfResult::createYes();
359: }
360:
361: if ($this->subtractedType !== null) {
362: $isSuperType = $this->subtractedType->isSuperTypeOf($otherType);
363: if ($isSuperType->yes()) {
364: return IsSuperTypeOfResult::createNo();
365: }
366: }
367:
368: return IsSuperTypeOfResult::createMaybe();
369: }
370:
371: public function isAcceptedBy(Type $acceptingType, bool $strictTypes): AcceptsResult
372: {
373: $isSuperType = $this->isSuperTypeOf($acceptingType)->toAcceptsResult();
374: if ($isSuperType->no()) {
375: return $isSuperType;
376: }
377: return AcceptsResult::createYes();
378: }
379:
380: public function getTemplateType(string $ancestorClassName, string $templateTypeName): Type
381: {
382: return new self();
383: }
384:
385: public function isObject(): TrinaryLogic
386: {
387: if ($this->subtractedType !== null) {
388: if ($this->subtractedType->isSuperTypeOf(new ObjectWithoutClassType())->yes()) {
389: return TrinaryLogic::createNo();
390: }
391: }
392: return TrinaryLogic::createMaybe();
393: }
394:
395: public function getClassStringType(): Type
396: {
397: return new ClassStringType();
398: }
399:
400: public function isEnum(): TrinaryLogic
401: {
402: if ($this->subtractedType !== null) {
403: if ($this->subtractedType->isSuperTypeOf(new ObjectWithoutClassType())->yes()) {
404: return TrinaryLogic::createNo();
405: }
406: }
407: return TrinaryLogic::createMaybe();
408: }
409:
410: public function canAccessProperties(): TrinaryLogic
411: {
412: return TrinaryLogic::createYes();
413: }
414:
415: public function hasProperty(string $propertyName): TrinaryLogic
416: {
417: return TrinaryLogic::createYes();
418: }
419:
420: public function getProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
421: {
422: return $this->getUnresolvedPropertyPrototype($propertyName, $scope)->getTransformedProperty();
423: }
424:
425: public function getUnresolvedPropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection
426: {
427: $property = new DummyPropertyReflection($propertyName);
428: return new CallbackUnresolvedPropertyPrototypeReflection(
429: $property,
430: $property->getDeclaringClass(),
431: false,
432: static fn (Type $type): Type => $type,
433: );
434: }
435:
436: public function hasInstanceProperty(string $propertyName): TrinaryLogic
437: {
438: return TrinaryLogic::createYes();
439: }
440:
441: public function getInstanceProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
442: {
443: return $this->getUnresolvedInstancePropertyPrototype($propertyName, $scope)->getTransformedProperty();
444: }
445:
446: public function getUnresolvedInstancePropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection
447: {
448: $property = new DummyPropertyReflection($propertyName);
449: return new CallbackUnresolvedPropertyPrototypeReflection(
450: $property,
451: $property->getDeclaringClass(),
452: false,
453: static fn (Type $type): Type => $type,
454: );
455: }
456:
457: public function hasStaticProperty(string $propertyName): TrinaryLogic
458: {
459: return TrinaryLogic::createYes();
460: }
461:
462: public function getStaticProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
463: {
464: return $this->getUnresolvedStaticPropertyPrototype($propertyName, $scope)->getTransformedProperty();
465: }
466:
467: public function getUnresolvedStaticPropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection
468: {
469: $property = new DummyPropertyReflection($propertyName);
470: return new CallbackUnresolvedPropertyPrototypeReflection(
471: $property,
472: $property->getDeclaringClass(),
473: false,
474: static fn (Type $type): Type => $type,
475: );
476: }
477:
478: public function canCallMethods(): TrinaryLogic
479: {
480: return TrinaryLogic::createYes();
481: }
482:
483: public function hasMethod(string $methodName): TrinaryLogic
484: {
485: return TrinaryLogic::createYes();
486: }
487:
488: public function getMethod(string $methodName, ClassMemberAccessAnswerer $scope): ExtendedMethodReflection
489: {
490: return $this->getUnresolvedMethodPrototype($methodName, $scope)->getTransformedMethod();
491: }
492:
493: public function getUnresolvedMethodPrototype(string $methodName, ClassMemberAccessAnswerer $scope): UnresolvedMethodPrototypeReflection
494: {
495: $method = new DummyMethodReflection($methodName);
496: return new CallbackUnresolvedMethodPrototypeReflection(
497: $method,
498: $method->getDeclaringClass(),
499: false,
500: static fn (Type $type): Type => $type,
501: );
502: }
503:
504: public function canAccessConstants(): TrinaryLogic
505: {
506: return TrinaryLogic::createYes();
507: }
508:
509: public function hasConstant(string $constantName): TrinaryLogic
510: {
511: return TrinaryLogic::createYes();
512: }
513:
514: public function getConstant(string $constantName): ClassConstantReflection
515: {
516: return new DummyClassConstantReflection($constantName);
517: }
518:
519: public function isCloneable(): TrinaryLogic
520: {
521: return TrinaryLogic::createYes();
522: }
523:
524: public function describe(VerbosityLevel $level): string
525: {
526: return $level->handle(
527: static fn (): string => 'mixed',
528: static fn (): string => 'mixed',
529: fn (): string => 'mixed' . $this->describeSubtractedType($this->subtractedType, $level),
530: function () use ($level): string {
531: $description = 'mixed' . $this->describeSubtractedType($this->subtractedType, $level);
532:
533: if ($this->isExplicitMixed) {
534: $description .= '=explicit';
535: } else {
536: $description .= '=implicit';
537: }
538:
539: return $description;
540: },
541: );
542: }
543:
544: public function toBoolean(): BooleanType
545: {
546: if ($this->subtractedType !== null) {
547: if ($this->subtractedType->isSuperTypeOf(StaticTypeFactory::falsey())->yes()) {
548: return new ConstantBooleanType(true);
549: }
550: }
551:
552: return new BooleanType();
553: }
554:
555: public function toNumber(): Type
556: {
557: return TypeCombinator::union(
558: $this->toInteger(),
559: $this->toFloat(),
560: );
561: }
562:
563: public function toAbsoluteNumber(): Type
564: {
565: return $this->toNumber()->toAbsoluteNumber();
566: }
567:
568: public function toInteger(): Type
569: {
570: $castsToZero = new UnionType([
571: new NullType(),
572: new ConstantBooleanType(false),
573: new ConstantIntegerType(0),
574: new ConstantArrayType([], []),
575: new StringType(),
576: new FloatType(), // every 0.x float casts to int(0)
577: ]);
578: if (
579: $this->subtractedType !== null
580: && $this->subtractedType->isSuperTypeOf($castsToZero)->yes()
581: ) {
582: return new UnionType([
583: IntegerRangeType::fromInterval(null, -1),
584: IntegerRangeType::fromInterval(1, null),
585: ]);
586: }
587:
588: return new IntegerType();
589: }
590:
591: public function toFloat(): Type
592: {
593: return new FloatType();
594: }
595:
596: public function toString(): Type
597: {
598: if ($this->subtractedType !== null) {
599: $castsToEmptyString = new UnionType([
600: new NullType(),
601: new ConstantBooleanType(false),
602: new ConstantStringType(''),
603: ]);
604: if ($this->subtractedType->isSuperTypeOf($castsToEmptyString)->yes()) {
605: $accessories = [
606: new StringType(),
607: new AccessoryNonEmptyStringType(),
608: ];
609:
610: $castsToZeroString = new UnionType([
611: new ConstantFloatType(0.0),
612: new ConstantStringType('0'),
613: new ConstantIntegerType(0),
614: ]);
615: if ($this->subtractedType->isSuperTypeOf($castsToZeroString)->yes()) {
616: $accessories[] = new AccessoryNonFalsyStringType();
617: }
618: return new IntersectionType(
619: $accessories,
620: );
621: }
622: }
623:
624: return new StringType();
625: }
626:
627: public function toArray(): Type
628: {
629: $mixed = new self($this->isExplicitMixed);
630:
631: return new ArrayType($mixed, $mixed);
632: }
633:
634: public function toArrayKey(): Type
635: {
636: return new BenevolentUnionType([new IntegerType(), new StringType()]);
637: }
638:
639: public function toCoercedArgumentType(bool $strictTypes): Type
640: {
641: return $this;
642: }
643:
644: public function isIterable(): TrinaryLogic
645: {
646: if ($this->subtractedType !== null) {
647: if ($this->subtractedType->isSuperTypeOf(new IterableType(new MixedType(), new MixedType()))->yes()) {
648: return TrinaryLogic::createNo();
649: }
650: }
651:
652: return TrinaryLogic::createMaybe();
653: }
654:
655: public function isIterableAtLeastOnce(): TrinaryLogic
656: {
657: return $this->isIterable();
658: }
659:
660: public function getArraySize(): Type
661: {
662: if ($this->isIterable()->no()) {
663: return new ErrorType();
664: }
665:
666: return IntegerRangeType::fromInterval(0, null);
667: }
668:
669: public function getIterableKeyType(): Type
670: {
671: return new self($this->isExplicitMixed);
672: }
673:
674: public function getFirstIterableKeyType(): Type
675: {
676: return new self($this->isExplicitMixed);
677: }
678:
679: public function getLastIterableKeyType(): Type
680: {
681: return new self($this->isExplicitMixed);
682: }
683:
684: public function getIterableValueType(): Type
685: {
686: return new self($this->isExplicitMixed);
687: }
688:
689: public function getFirstIterableValueType(): Type
690: {
691: return new self($this->isExplicitMixed);
692: }
693:
694: public function getLastIterableValueType(): Type
695: {
696: return new self($this->isExplicitMixed);
697: }
698:
699: public function isOffsetAccessible(): TrinaryLogic
700: {
701: if ($this->subtractedType !== null) {
702: $offsetAccessibles = new UnionType([
703: new StringType(),
704: new ArrayType(new MixedType(), new MixedType()),
705: new ObjectType(ArrayAccess::class),
706: ]);
707:
708: if ($this->subtractedType->isSuperTypeOf($offsetAccessibles)->yes()) {
709: return TrinaryLogic::createNo();
710: }
711: }
712: return TrinaryLogic::createMaybe();
713: }
714:
715: public function isOffsetAccessLegal(): TrinaryLogic
716: {
717: if ($this->subtractedType !== null) {
718: if ($this->subtractedType->isSuperTypeOf(new ObjectWithoutClassType())->yes()) {
719: return TrinaryLogic::createYes();
720: }
721: }
722: return TrinaryLogic::createMaybe();
723: }
724:
725: public function hasOffsetValueType(Type $offsetType): TrinaryLogic
726: {
727: if ($this->isOffsetAccessible()->no()) {
728: return TrinaryLogic::createNo();
729: }
730:
731: return TrinaryLogic::createMaybe();
732: }
733:
734: public function getOffsetValueType(Type $offsetType): Type
735: {
736: return new self($this->isExplicitMixed);
737: }
738:
739: public function isExplicitMixed(): bool
740: {
741: return $this->isExplicitMixed;
742: }
743:
744: public function subtract(Type $type): Type
745: {
746: if ($type instanceof self && !$type instanceof TemplateType) {
747: return new NeverType();
748: }
749: if ($this->subtractedType !== null) {
750: $type = TypeCombinator::union($this->subtractedType, $type);
751: }
752:
753: return new self($this->isExplicitMixed, $type);
754: }
755:
756: public function getTypeWithoutSubtractedType(): Type
757: {
758: return new self($this->isExplicitMixed);
759: }
760:
761: public function changeSubtractedType(?Type $subtractedType): Type
762: {
763: return new self($this->isExplicitMixed, $subtractedType);
764: }
765:
766: public function getSubtractedType(): ?Type
767: {
768: return $this->subtractedType;
769: }
770:
771: public function traverse(callable $cb): Type
772: {
773: return $this;
774: }
775:
776: public function traverseSimultaneously(Type $right, callable $cb): Type
777: {
778: return $this;
779: }
780:
781: public function isArray(): TrinaryLogic
782: {
783: if ($this->subtractedType !== null) {
784: if ($this->subtractedType->isSuperTypeOf(new ArrayType(new MixedType(), new MixedType()))->yes()) {
785: return TrinaryLogic::createNo();
786: }
787: }
788:
789: return TrinaryLogic::createMaybe();
790: }
791:
792: public function isConstantArray(): TrinaryLogic
793: {
794: return $this->isArray();
795: }
796:
797: public function isOversizedArray(): TrinaryLogic
798: {
799: if ($this->subtractedType !== null) {
800: $oversizedArray = new IntersectionType([
801: new ArrayType(new MixedType(), new MixedType()),
802: new OversizedArrayType(),
803: ]);
804:
805: if ($this->subtractedType->isSuperTypeOf($oversizedArray)->yes()) {
806: return TrinaryLogic::createNo();
807: }
808: }
809:
810: return TrinaryLogic::createMaybe();
811: }
812:
813: public function isList(): TrinaryLogic
814: {
815: if ($this->subtractedType !== null) {
816: $list = new IntersectionType([
817: new ArrayType(IntegerRangeType::createAllGreaterThanOrEqualTo(0), new MixedType()),
818: new AccessoryArrayListType(),
819: ]);
820:
821: if ($this->subtractedType->isSuperTypeOf($list)->yes()) {
822: return TrinaryLogic::createNo();
823: }
824: }
825:
826: return TrinaryLogic::createMaybe();
827: }
828:
829: public function isNull(): TrinaryLogic
830: {
831: if ($this->subtractedType !== null) {
832: if ($this->subtractedType->isSuperTypeOf(new NullType())->yes()) {
833: return TrinaryLogic::createNo();
834: }
835: }
836:
837: return TrinaryLogic::createMaybe();
838: }
839:
840: public function isConstantValue(): TrinaryLogic
841: {
842: return TrinaryLogic::createNo();
843: }
844:
845: public function isConstantScalarValue(): TrinaryLogic
846: {
847: return TrinaryLogic::createNo();
848: }
849:
850: public function getConstantScalarTypes(): array
851: {
852: return [];
853: }
854:
855: public function getConstantScalarValues(): array
856: {
857: return [];
858: }
859:
860: public function isTrue(): TrinaryLogic
861: {
862: if ($this->subtractedType !== null) {
863: if ($this->subtractedType->isSuperTypeOf(new ConstantBooleanType(true))->yes()) {
864: return TrinaryLogic::createNo();
865: }
866: }
867:
868: return TrinaryLogic::createMaybe();
869: }
870:
871: public function isFalse(): TrinaryLogic
872: {
873: if ($this->subtractedType !== null) {
874: if ($this->subtractedType->isSuperTypeOf(new ConstantBooleanType(false))->yes()) {
875: return TrinaryLogic::createNo();
876: }
877: }
878:
879: return TrinaryLogic::createMaybe();
880: }
881:
882: public function isBoolean(): TrinaryLogic
883: {
884: if ($this->subtractedType !== null) {
885: if ($this->subtractedType->isSuperTypeOf(new BooleanType())->yes()) {
886: return TrinaryLogic::createNo();
887: }
888: }
889:
890: return TrinaryLogic::createMaybe();
891: }
892:
893: public function isFloat(): TrinaryLogic
894: {
895: if ($this->subtractedType !== null) {
896: if ($this->subtractedType->isSuperTypeOf(new FloatType())->yes()) {
897: return TrinaryLogic::createNo();
898: }
899: }
900:
901: return TrinaryLogic::createMaybe();
902: }
903:
904: public function isInteger(): TrinaryLogic
905: {
906: if ($this->subtractedType !== null) {
907: if ($this->subtractedType->isSuperTypeOf(new IntegerType())->yes()) {
908: return TrinaryLogic::createNo();
909: }
910: }
911:
912: return TrinaryLogic::createMaybe();
913: }
914:
915: public function isString(): TrinaryLogic
916: {
917: if ($this->subtractedType !== null) {
918: if ($this->subtractedType->isSuperTypeOf(new StringType())->yes()) {
919: return TrinaryLogic::createNo();
920: }
921: }
922: return TrinaryLogic::createMaybe();
923: }
924:
925: public function isNumericString(): TrinaryLogic
926: {
927: if ($this->subtractedType !== null) {
928: $numericString = new IntersectionType([
929: new StringType(),
930: new AccessoryNumericStringType(),
931: ]);
932:
933: if ($this->subtractedType->isSuperTypeOf($numericString)->yes()) {
934: return TrinaryLogic::createNo();
935: }
936: }
937:
938: return TrinaryLogic::createMaybe();
939: }
940:
941: public function isNonEmptyString(): TrinaryLogic
942: {
943: if ($this->subtractedType !== null) {
944: $nonEmptyString = new IntersectionType([
945: new StringType(),
946: new AccessoryNonEmptyStringType(),
947: ]);
948:
949: if ($this->subtractedType->isSuperTypeOf($nonEmptyString)->yes()) {
950: return TrinaryLogic::createNo();
951: }
952: }
953:
954: return TrinaryLogic::createMaybe();
955: }
956:
957: public function isNonFalsyString(): TrinaryLogic
958: {
959: if ($this->subtractedType !== null) {
960: $nonFalsyString = new IntersectionType([
961: new StringType(),
962: new AccessoryNonFalsyStringType(),
963: ]);
964:
965: if ($this->subtractedType->isSuperTypeOf($nonFalsyString)->yes()) {
966: return TrinaryLogic::createNo();
967: }
968: }
969:
970: return TrinaryLogic::createMaybe();
971: }
972:
973: public function isLiteralString(): TrinaryLogic
974: {
975: if ($this->subtractedType !== null) {
976: $literalString = new IntersectionType([
977: new StringType(),
978: new AccessoryLiteralStringType(),
979: ]);
980:
981: if ($this->subtractedType->isSuperTypeOf($literalString)->yes()) {
982: return TrinaryLogic::createNo();
983: }
984: }
985:
986: return TrinaryLogic::createMaybe();
987: }
988:
989: public function isLowercaseString(): TrinaryLogic
990: {
991: if ($this->subtractedType !== null) {
992: $lowercaseString = new IntersectionType([
993: new StringType(),
994: new AccessoryLowercaseStringType(),
995: ]);
996:
997: if ($this->subtractedType->isSuperTypeOf($lowercaseString)->yes()) {
998: return TrinaryLogic::createNo();
999: }
1000: }
1001:
1002: return TrinaryLogic::createMaybe();
1003: }
1004:
1005: public function isUppercaseString(): TrinaryLogic
1006: {
1007: if ($this->subtractedType !== null) {
1008: $uppercaseString = new IntersectionType([
1009: new StringType(),
1010: new AccessoryUppercaseStringType(),
1011: ]);
1012:
1013: if ($this->subtractedType->isSuperTypeOf($uppercaseString)->yes()) {
1014: return TrinaryLogic::createNo();
1015: }
1016: }
1017:
1018: return TrinaryLogic::createMaybe();
1019: }
1020:
1021: public function isClassString(): TrinaryLogic
1022: {
1023: if ($this->subtractedType !== null) {
1024: if ($this->subtractedType->isSuperTypeOf(new StringType())->yes()) {
1025: return TrinaryLogic::createNo();
1026: }
1027: if ($this->subtractedType->isSuperTypeOf(new ClassStringType())->yes()) {
1028: return TrinaryLogic::createNo();
1029: }
1030: }
1031:
1032: return TrinaryLogic::createMaybe();
1033: }
1034:
1035: public function getClassStringObjectType(): Type
1036: {
1037: if (!$this->isClassString()->no()) {
1038: return new ObjectWithoutClassType();
1039: }
1040:
1041: return new ErrorType();
1042: }
1043:
1044: public function getObjectTypeOrClassStringObjectType(): Type
1045: {
1046: $objectOrClass = new UnionType([
1047: new ObjectWithoutClassType(),
1048: new ClassStringType(),
1049: ]);
1050: if (!$this->isSuperTypeOf($objectOrClass)->no()) {
1051: return new ObjectWithoutClassType();
1052: }
1053:
1054: return new ErrorType();
1055: }
1056:
1057: public function isVoid(): TrinaryLogic
1058: {
1059: if ($this->subtractedType !== null) {
1060: if ($this->subtractedType->isSuperTypeOf(new VoidType())->yes()) {
1061: return TrinaryLogic::createNo();
1062: }
1063: }
1064:
1065: return TrinaryLogic::createMaybe();
1066: }
1067:
1068: public function isScalar(): TrinaryLogic
1069: {
1070: if ($this->subtractedType !== null) {
1071: if ($this->subtractedType->isSuperTypeOf(new UnionType([new BooleanType(), new FloatType(), new IntegerType(), new StringType()]))->yes()) {
1072: return TrinaryLogic::createNo();
1073: }
1074: }
1075:
1076: return TrinaryLogic::createMaybe();
1077: }
1078:
1079: public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
1080: {
1081: return new BooleanType();
1082: }
1083:
1084: public function tryRemove(Type $typeToRemove): ?Type
1085: {
1086: if ($this->isSuperTypeOf($typeToRemove)->yes()) {
1087: return $this->subtract($typeToRemove);
1088: }
1089:
1090: return null;
1091: }
1092:
1093: public function exponentiate(Type $exponent): Type
1094: {
1095: return new BenevolentUnionType([
1096: new FloatType(),
1097: new IntegerType(),
1098: ]);
1099: }
1100:
1101: public function getFiniteTypes(): array
1102: {
1103: return [];
1104: }
1105:
1106: public function toPhpDocNode(): TypeNode
1107: {
1108: return new IdentifierTypeNode('mixed');
1109: }
1110:
1111: public function hasTemplateOrLateResolvableType(): bool
1112: {
1113: return false;
1114: }
1115:
1116: }
1117: