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