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 isEnum(): TrinaryLogic
396: {
397: if ($this->subtractedType !== null) {
398: if ($this->subtractedType->isSuperTypeOf(new ObjectWithoutClassType())->yes()) {
399: return TrinaryLogic::createNo();
400: }
401: }
402: return TrinaryLogic::createMaybe();
403: }
404:
405: public function canAccessProperties(): TrinaryLogic
406: {
407: return TrinaryLogic::createYes();
408: }
409:
410: public function hasProperty(string $propertyName): TrinaryLogic
411: {
412: return TrinaryLogic::createYes();
413: }
414:
415: public function getProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
416: {
417: return $this->getUnresolvedPropertyPrototype($propertyName, $scope)->getTransformedProperty();
418: }
419:
420: public function getUnresolvedPropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection
421: {
422: $property = new DummyPropertyReflection($propertyName);
423: return new CallbackUnresolvedPropertyPrototypeReflection(
424: $property,
425: $property->getDeclaringClass(),
426: false,
427: static fn (Type $type): Type => $type,
428: );
429: }
430:
431: public function hasInstanceProperty(string $propertyName): TrinaryLogic
432: {
433: return TrinaryLogic::createYes();
434: }
435:
436: public function getInstanceProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
437: {
438: return $this->getUnresolvedInstancePropertyPrototype($propertyName, $scope)->getTransformedProperty();
439: }
440:
441: public function getUnresolvedInstancePropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection
442: {
443: $property = new DummyPropertyReflection($propertyName);
444: return new CallbackUnresolvedPropertyPrototypeReflection(
445: $property,
446: $property->getDeclaringClass(),
447: false,
448: static fn (Type $type): Type => $type,
449: );
450: }
451:
452: public function hasStaticProperty(string $propertyName): TrinaryLogic
453: {
454: return TrinaryLogic::createYes();
455: }
456:
457: public function getStaticProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection
458: {
459: return $this->getUnresolvedStaticPropertyPrototype($propertyName, $scope)->getTransformedProperty();
460: }
461:
462: public function getUnresolvedStaticPropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection
463: {
464: $property = new DummyPropertyReflection($propertyName);
465: return new CallbackUnresolvedPropertyPrototypeReflection(
466: $property,
467: $property->getDeclaringClass(),
468: false,
469: static fn (Type $type): Type => $type,
470: );
471: }
472:
473: public function canCallMethods(): TrinaryLogic
474: {
475: return TrinaryLogic::createYes();
476: }
477:
478: public function hasMethod(string $methodName): TrinaryLogic
479: {
480: return TrinaryLogic::createYes();
481: }
482:
483: public function getMethod(string $methodName, ClassMemberAccessAnswerer $scope): ExtendedMethodReflection
484: {
485: return $this->getUnresolvedMethodPrototype($methodName, $scope)->getTransformedMethod();
486: }
487:
488: public function getUnresolvedMethodPrototype(string $methodName, ClassMemberAccessAnswerer $scope): UnresolvedMethodPrototypeReflection
489: {
490: $method = new DummyMethodReflection($methodName);
491: return new CallbackUnresolvedMethodPrototypeReflection(
492: $method,
493: $method->getDeclaringClass(),
494: false,
495: static fn (Type $type): Type => $type,
496: );
497: }
498:
499: public function canAccessConstants(): TrinaryLogic
500: {
501: return TrinaryLogic::createYes();
502: }
503:
504: public function hasConstant(string $constantName): TrinaryLogic
505: {
506: return TrinaryLogic::createYes();
507: }
508:
509: public function getConstant(string $constantName): ClassConstantReflection
510: {
511: return new DummyClassConstantReflection($constantName);
512: }
513:
514: public function isCloneable(): TrinaryLogic
515: {
516: return TrinaryLogic::createYes();
517: }
518:
519: public function describe(VerbosityLevel $level): string
520: {
521: return $level->handle(
522: static fn (): string => 'mixed',
523: static fn (): string => 'mixed',
524: fn (): string => 'mixed' . $this->describeSubtractedType($this->subtractedType, $level),
525: function () use ($level): string {
526: $description = 'mixed' . $this->describeSubtractedType($this->subtractedType, $level);
527:
528: if ($this->isExplicitMixed) {
529: $description .= '=explicit';
530: } else {
531: $description .= '=implicit';
532: }
533:
534: return $description;
535: },
536: );
537: }
538:
539: public function toBoolean(): BooleanType
540: {
541: if ($this->subtractedType !== null) {
542: if ($this->subtractedType->isSuperTypeOf(StaticTypeFactory::falsey())->yes()) {
543: return new ConstantBooleanType(true);
544: }
545: }
546:
547: return new BooleanType();
548: }
549:
550: public function toNumber(): Type
551: {
552: return TypeCombinator::union(
553: $this->toInteger(),
554: $this->toFloat(),
555: );
556: }
557:
558: public function toAbsoluteNumber(): Type
559: {
560: return $this->toNumber()->toAbsoluteNumber();
561: }
562:
563: public function toInteger(): Type
564: {
565: $castsToZero = new UnionType([
566: new NullType(),
567: new ConstantBooleanType(false),
568: new ConstantIntegerType(0),
569: new ConstantArrayType([], []),
570: new StringType(),
571: new FloatType(), // every 0.x float casts to int(0)
572: ]);
573: if (
574: $this->subtractedType !== null
575: && $this->subtractedType->isSuperTypeOf($castsToZero)->yes()
576: ) {
577: return new UnionType([
578: IntegerRangeType::fromInterval(null, -1),
579: IntegerRangeType::fromInterval(1, null),
580: ]);
581: }
582:
583: return new IntegerType();
584: }
585:
586: public function toFloat(): Type
587: {
588: return new FloatType();
589: }
590:
591: public function toString(): Type
592: {
593: if ($this->subtractedType !== null) {
594: $castsToEmptyString = new UnionType([
595: new NullType(),
596: new ConstantBooleanType(false),
597: new ConstantStringType(''),
598: ]);
599: if ($this->subtractedType->isSuperTypeOf($castsToEmptyString)->yes()) {
600: $accessories = [
601: new StringType(),
602: new AccessoryNonEmptyStringType(),
603: ];
604:
605: $castsToZeroString = new UnionType([
606: new ConstantFloatType(0.0),
607: new ConstantStringType('0'),
608: new ConstantIntegerType(0),
609: ]);
610: if ($this->subtractedType->isSuperTypeOf($castsToZeroString)->yes()) {
611: $accessories[] = new AccessoryNonFalsyStringType();
612: }
613: return new IntersectionType(
614: $accessories,
615: );
616: }
617: }
618:
619: return new StringType();
620: }
621:
622: public function toArray(): Type
623: {
624: $mixed = new self($this->isExplicitMixed);
625:
626: return new ArrayType($mixed, $mixed);
627: }
628:
629: public function toArrayKey(): Type
630: {
631: return new BenevolentUnionType([new IntegerType(), new StringType()]);
632: }
633:
634: public function toCoercedArgumentType(bool $strictTypes): Type
635: {
636: return $this;
637: }
638:
639: public function isIterable(): TrinaryLogic
640: {
641: if ($this->subtractedType !== null) {
642: if ($this->subtractedType->isSuperTypeOf(new IterableType(new MixedType(), new MixedType()))->yes()) {
643: return TrinaryLogic::createNo();
644: }
645: }
646:
647: return TrinaryLogic::createMaybe();
648: }
649:
650: public function isIterableAtLeastOnce(): TrinaryLogic
651: {
652: return $this->isIterable();
653: }
654:
655: public function getArraySize(): Type
656: {
657: if ($this->isIterable()->no()) {
658: return new ErrorType();
659: }
660:
661: return IntegerRangeType::fromInterval(0, null);
662: }
663:
664: public function getIterableKeyType(): Type
665: {
666: return new self($this->isExplicitMixed);
667: }
668:
669: public function getFirstIterableKeyType(): Type
670: {
671: return new self($this->isExplicitMixed);
672: }
673:
674: public function getLastIterableKeyType(): Type
675: {
676: return new self($this->isExplicitMixed);
677: }
678:
679: public function getIterableValueType(): Type
680: {
681: return new self($this->isExplicitMixed);
682: }
683:
684: public function getFirstIterableValueType(): Type
685: {
686: return new self($this->isExplicitMixed);
687: }
688:
689: public function getLastIterableValueType(): Type
690: {
691: return new self($this->isExplicitMixed);
692: }
693:
694: public function isOffsetAccessible(): TrinaryLogic
695: {
696: if ($this->subtractedType !== null) {
697: $offsetAccessibles = new UnionType([
698: new StringType(),
699: new ArrayType(new MixedType(), new MixedType()),
700: new ObjectType(ArrayAccess::class),
701: ]);
702:
703: if ($this->subtractedType->isSuperTypeOf($offsetAccessibles)->yes()) {
704: return TrinaryLogic::createNo();
705: }
706: }
707: return TrinaryLogic::createMaybe();
708: }
709:
710: public function isOffsetAccessLegal(): TrinaryLogic
711: {
712: if ($this->subtractedType !== null) {
713: if ($this->subtractedType->isSuperTypeOf(new ObjectWithoutClassType())->yes()) {
714: return TrinaryLogic::createYes();
715: }
716: }
717: return TrinaryLogic::createMaybe();
718: }
719:
720: public function hasOffsetValueType(Type $offsetType): TrinaryLogic
721: {
722: if ($this->isOffsetAccessible()->no()) {
723: return TrinaryLogic::createNo();
724: }
725:
726: return TrinaryLogic::createMaybe();
727: }
728:
729: public function getOffsetValueType(Type $offsetType): Type
730: {
731: return new self($this->isExplicitMixed);
732: }
733:
734: public function isExplicitMixed(): bool
735: {
736: return $this->isExplicitMixed;
737: }
738:
739: public function subtract(Type $type): Type
740: {
741: if ($type instanceof self && !$type instanceof TemplateType) {
742: return new NeverType();
743: }
744: if ($this->subtractedType !== null) {
745: $type = TypeCombinator::union($this->subtractedType, $type);
746: }
747:
748: return new self($this->isExplicitMixed, $type);
749: }
750:
751: public function getTypeWithoutSubtractedType(): Type
752: {
753: return new self($this->isExplicitMixed);
754: }
755:
756: public function changeSubtractedType(?Type $subtractedType): Type
757: {
758: return new self($this->isExplicitMixed, $subtractedType);
759: }
760:
761: public function getSubtractedType(): ?Type
762: {
763: return $this->subtractedType;
764: }
765:
766: public function traverse(callable $cb): Type
767: {
768: return $this;
769: }
770:
771: public function traverseSimultaneously(Type $right, callable $cb): Type
772: {
773: return $this;
774: }
775:
776: public function isArray(): TrinaryLogic
777: {
778: if ($this->subtractedType !== null) {
779: if ($this->subtractedType->isSuperTypeOf(new ArrayType(new MixedType(), new MixedType()))->yes()) {
780: return TrinaryLogic::createNo();
781: }
782: }
783:
784: return TrinaryLogic::createMaybe();
785: }
786:
787: public function isConstantArray(): TrinaryLogic
788: {
789: return $this->isArray();
790: }
791:
792: public function isOversizedArray(): TrinaryLogic
793: {
794: if ($this->subtractedType !== null) {
795: $oversizedArray = new IntersectionType([
796: new ArrayType(new MixedType(), new MixedType()),
797: new OversizedArrayType(),
798: ]);
799:
800: if ($this->subtractedType->isSuperTypeOf($oversizedArray)->yes()) {
801: return TrinaryLogic::createNo();
802: }
803: }
804:
805: return TrinaryLogic::createMaybe();
806: }
807:
808: public function isList(): TrinaryLogic
809: {
810: if ($this->subtractedType !== null) {
811: $list = new IntersectionType([
812: new ArrayType(IntegerRangeType::createAllGreaterThanOrEqualTo(0), new MixedType()),
813: new AccessoryArrayListType(),
814: ]);
815:
816: if ($this->subtractedType->isSuperTypeOf($list)->yes()) {
817: return TrinaryLogic::createNo();
818: }
819: }
820:
821: return TrinaryLogic::createMaybe();
822: }
823:
824: public function isNull(): TrinaryLogic
825: {
826: if ($this->subtractedType !== null) {
827: if ($this->subtractedType->isSuperTypeOf(new NullType())->yes()) {
828: return TrinaryLogic::createNo();
829: }
830: }
831:
832: return TrinaryLogic::createMaybe();
833: }
834:
835: public function isConstantValue(): TrinaryLogic
836: {
837: return TrinaryLogic::createNo();
838: }
839:
840: public function isConstantScalarValue(): TrinaryLogic
841: {
842: return TrinaryLogic::createNo();
843: }
844:
845: public function getConstantScalarTypes(): array
846: {
847: return [];
848: }
849:
850: public function getConstantScalarValues(): array
851: {
852: return [];
853: }
854:
855: public function isTrue(): TrinaryLogic
856: {
857: if ($this->subtractedType !== null) {
858: if ($this->subtractedType->isSuperTypeOf(new ConstantBooleanType(true))->yes()) {
859: return TrinaryLogic::createNo();
860: }
861: }
862:
863: return TrinaryLogic::createMaybe();
864: }
865:
866: public function isFalse(): TrinaryLogic
867: {
868: if ($this->subtractedType !== null) {
869: if ($this->subtractedType->isSuperTypeOf(new ConstantBooleanType(false))->yes()) {
870: return TrinaryLogic::createNo();
871: }
872: }
873:
874: return TrinaryLogic::createMaybe();
875: }
876:
877: public function isBoolean(): TrinaryLogic
878: {
879: if ($this->subtractedType !== null) {
880: if ($this->subtractedType->isSuperTypeOf(new BooleanType())->yes()) {
881: return TrinaryLogic::createNo();
882: }
883: }
884:
885: return TrinaryLogic::createMaybe();
886: }
887:
888: public function isFloat(): TrinaryLogic
889: {
890: if ($this->subtractedType !== null) {
891: if ($this->subtractedType->isSuperTypeOf(new FloatType())->yes()) {
892: return TrinaryLogic::createNo();
893: }
894: }
895:
896: return TrinaryLogic::createMaybe();
897: }
898:
899: public function isInteger(): TrinaryLogic
900: {
901: if ($this->subtractedType !== null) {
902: if ($this->subtractedType->isSuperTypeOf(new IntegerType())->yes()) {
903: return TrinaryLogic::createNo();
904: }
905: }
906:
907: return TrinaryLogic::createMaybe();
908: }
909:
910: public function isString(): TrinaryLogic
911: {
912: if ($this->subtractedType !== null) {
913: if ($this->subtractedType->isSuperTypeOf(new StringType())->yes()) {
914: return TrinaryLogic::createNo();
915: }
916: }
917: return TrinaryLogic::createMaybe();
918: }
919:
920: public function isNumericString(): TrinaryLogic
921: {
922: if ($this->subtractedType !== null) {
923: $numericString = new IntersectionType([
924: new StringType(),
925: new AccessoryNumericStringType(),
926: ]);
927:
928: if ($this->subtractedType->isSuperTypeOf($numericString)->yes()) {
929: return TrinaryLogic::createNo();
930: }
931: }
932:
933: return TrinaryLogic::createMaybe();
934: }
935:
936: public function isNonEmptyString(): TrinaryLogic
937: {
938: if ($this->subtractedType !== null) {
939: $nonEmptyString = new IntersectionType([
940: new StringType(),
941: new AccessoryNonEmptyStringType(),
942: ]);
943:
944: if ($this->subtractedType->isSuperTypeOf($nonEmptyString)->yes()) {
945: return TrinaryLogic::createNo();
946: }
947: }
948:
949: return TrinaryLogic::createMaybe();
950: }
951:
952: public function isNonFalsyString(): TrinaryLogic
953: {
954: if ($this->subtractedType !== null) {
955: $nonFalsyString = new IntersectionType([
956: new StringType(),
957: new AccessoryNonFalsyStringType(),
958: ]);
959:
960: if ($this->subtractedType->isSuperTypeOf($nonFalsyString)->yes()) {
961: return TrinaryLogic::createNo();
962: }
963: }
964:
965: return TrinaryLogic::createMaybe();
966: }
967:
968: public function isLiteralString(): TrinaryLogic
969: {
970: if ($this->subtractedType !== null) {
971: $literalString = new IntersectionType([
972: new StringType(),
973: new AccessoryLiteralStringType(),
974: ]);
975:
976: if ($this->subtractedType->isSuperTypeOf($literalString)->yes()) {
977: return TrinaryLogic::createNo();
978: }
979: }
980:
981: return TrinaryLogic::createMaybe();
982: }
983:
984: public function isLowercaseString(): TrinaryLogic
985: {
986: if ($this->subtractedType !== null) {
987: $lowercaseString = new IntersectionType([
988: new StringType(),
989: new AccessoryLowercaseStringType(),
990: ]);
991:
992: if ($this->subtractedType->isSuperTypeOf($lowercaseString)->yes()) {
993: return TrinaryLogic::createNo();
994: }
995: }
996:
997: return TrinaryLogic::createMaybe();
998: }
999:
1000: public function isUppercaseString(): TrinaryLogic
1001: {
1002: if ($this->subtractedType !== null) {
1003: $uppercaseString = new IntersectionType([
1004: new StringType(),
1005: new AccessoryUppercaseStringType(),
1006: ]);
1007:
1008: if ($this->subtractedType->isSuperTypeOf($uppercaseString)->yes()) {
1009: return TrinaryLogic::createNo();
1010: }
1011: }
1012:
1013: return TrinaryLogic::createMaybe();
1014: }
1015:
1016: public function isClassString(): TrinaryLogic
1017: {
1018: if ($this->subtractedType !== null) {
1019: if ($this->subtractedType->isSuperTypeOf(new StringType())->yes()) {
1020: return TrinaryLogic::createNo();
1021: }
1022: if ($this->subtractedType->isSuperTypeOf(new ClassStringType())->yes()) {
1023: return TrinaryLogic::createNo();
1024: }
1025: }
1026:
1027: return TrinaryLogic::createMaybe();
1028: }
1029:
1030: public function getClassStringObjectType(): Type
1031: {
1032: if (!$this->isClassString()->no()) {
1033: return new ObjectWithoutClassType();
1034: }
1035:
1036: return new ErrorType();
1037: }
1038:
1039: public function getObjectTypeOrClassStringObjectType(): Type
1040: {
1041: $objectOrClass = new UnionType([
1042: new ObjectWithoutClassType(),
1043: new ClassStringType(),
1044: ]);
1045: if (!$this->isSuperTypeOf($objectOrClass)->no()) {
1046: return new ObjectWithoutClassType();
1047: }
1048:
1049: return new ErrorType();
1050: }
1051:
1052: public function isVoid(): TrinaryLogic
1053: {
1054: if ($this->subtractedType !== null) {
1055: if ($this->subtractedType->isSuperTypeOf(new VoidType())->yes()) {
1056: return TrinaryLogic::createNo();
1057: }
1058: }
1059:
1060: return TrinaryLogic::createMaybe();
1061: }
1062:
1063: public function isScalar(): TrinaryLogic
1064: {
1065: if ($this->subtractedType !== null) {
1066: if ($this->subtractedType->isSuperTypeOf(new UnionType([new BooleanType(), new FloatType(), new IntegerType(), new StringType()]))->yes()) {
1067: return TrinaryLogic::createNo();
1068: }
1069: }
1070:
1071: return TrinaryLogic::createMaybe();
1072: }
1073:
1074: public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
1075: {
1076: return new BooleanType();
1077: }
1078:
1079: public function tryRemove(Type $typeToRemove): ?Type
1080: {
1081: if ($this->isSuperTypeOf($typeToRemove)->yes()) {
1082: return $this->subtract($typeToRemove);
1083: }
1084:
1085: return null;
1086: }
1087:
1088: public function exponentiate(Type $exponent): Type
1089: {
1090: return new BenevolentUnionType([
1091: new FloatType(),
1092: new IntegerType(),
1093: ]);
1094: }
1095:
1096: public function getFiniteTypes(): array
1097: {
1098: return [];
1099: }
1100:
1101: public function toPhpDocNode(): TypeNode
1102: {
1103: return new IdentifierTypeNode('mixed');
1104: }
1105:
1106: public function hasTemplateOrLateResolvableType(): bool
1107: {
1108: return false;
1109: }
1110:
1111: }
1112: