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