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