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 hasOffsetValueType(Type $offsetType): TrinaryLogic
570: {
571: if ($this->isOffsetAccessible()->no()) {
572: return TrinaryLogic::createNo();
573: }
574:
575: return TrinaryLogic::createMaybe();
576: }
577:
578: public function getOffsetValueType(Type $offsetType): Type
579: {
580: return new self($this->isExplicitMixed);
581: }
582:
583: public function isExplicitMixed(): bool
584: {
585: return $this->isExplicitMixed;
586: }
587:
588: public function subtract(Type $type): Type
589: {
590: if ($type instanceof self && !$type instanceof TemplateType) {
591: return new NeverType();
592: }
593: if ($this->subtractedType !== null) {
594: $type = TypeCombinator::union($this->subtractedType, $type);
595: }
596:
597: return new self($this->isExplicitMixed, $type);
598: }
599:
600: public function getTypeWithoutSubtractedType(): Type
601: {
602: return new self($this->isExplicitMixed);
603: }
604:
605: public function changeSubtractedType(?Type $subtractedType): Type
606: {
607: return new self($this->isExplicitMixed, $subtractedType);
608: }
609:
610: public function getSubtractedType(): ?Type
611: {
612: return $this->subtractedType;
613: }
614:
615: public function traverse(callable $cb): Type
616: {
617: return $this;
618: }
619:
620: public function traverseSimultaneously(Type $right, callable $cb): Type
621: {
622: return $this;
623: }
624:
625: public function isArray(): TrinaryLogic
626: {
627: if ($this->subtractedType !== null) {
628: if ($this->subtractedType->isSuperTypeOf(new ArrayType(new MixedType(), new MixedType()))->yes()) {
629: return TrinaryLogic::createNo();
630: }
631: }
632:
633: return TrinaryLogic::createMaybe();
634: }
635:
636: public function isConstantArray(): TrinaryLogic
637: {
638: return $this->isArray();
639: }
640:
641: public function isOversizedArray(): TrinaryLogic
642: {
643: if ($this->subtractedType !== null) {
644: $oversizedArray = TypeCombinator::intersect(
645: new ArrayType(new MixedType(), new MixedType()),
646: new OversizedArrayType(),
647: );
648:
649: if ($this->subtractedType->isSuperTypeOf($oversizedArray)->yes()) {
650: return TrinaryLogic::createNo();
651: }
652: }
653:
654: return TrinaryLogic::createMaybe();
655: }
656:
657: public function isList(): TrinaryLogic
658: {
659: if ($this->subtractedType !== null) {
660: $list = TypeCombinator::intersect(
661: new ArrayType(new IntegerType(), new MixedType()),
662: new AccessoryArrayListType(),
663: );
664:
665: if ($this->subtractedType->isSuperTypeOf($list)->yes()) {
666: return TrinaryLogic::createNo();
667: }
668: }
669:
670: return TrinaryLogic::createMaybe();
671: }
672:
673: public function isNull(): TrinaryLogic
674: {
675: if ($this->subtractedType !== null) {
676: if ($this->subtractedType->isSuperTypeOf(new NullType())->yes()) {
677: return TrinaryLogic::createNo();
678: }
679: }
680:
681: return TrinaryLogic::createMaybe();
682: }
683:
684: public function isConstantValue(): TrinaryLogic
685: {
686: return TrinaryLogic::createNo();
687: }
688:
689: public function isConstantScalarValue(): TrinaryLogic
690: {
691: return TrinaryLogic::createNo();
692: }
693:
694: public function getConstantScalarTypes(): array
695: {
696: return [];
697: }
698:
699: public function getConstantScalarValues(): array
700: {
701: return [];
702: }
703:
704: public function isTrue(): TrinaryLogic
705: {
706: if ($this->subtractedType !== null) {
707: if ($this->subtractedType->isSuperTypeOf(new ConstantBooleanType(true))->yes()) {
708: return TrinaryLogic::createNo();
709: }
710: }
711:
712: return TrinaryLogic::createMaybe();
713: }
714:
715: public function isFalse(): TrinaryLogic
716: {
717: if ($this->subtractedType !== null) {
718: if ($this->subtractedType->isSuperTypeOf(new ConstantBooleanType(false))->yes()) {
719: return TrinaryLogic::createNo();
720: }
721: }
722:
723: return TrinaryLogic::createMaybe();
724: }
725:
726: public function isBoolean(): TrinaryLogic
727: {
728: if ($this->subtractedType !== null) {
729: if ($this->subtractedType->isSuperTypeOf(new BooleanType())->yes()) {
730: return TrinaryLogic::createNo();
731: }
732: }
733:
734: return TrinaryLogic::createMaybe();
735: }
736:
737: public function isFloat(): TrinaryLogic
738: {
739: if ($this->subtractedType !== null) {
740: if ($this->subtractedType->isSuperTypeOf(new FloatType())->yes()) {
741: return TrinaryLogic::createNo();
742: }
743: }
744:
745: return TrinaryLogic::createMaybe();
746: }
747:
748: public function isInteger(): TrinaryLogic
749: {
750: if ($this->subtractedType !== null) {
751: if ($this->subtractedType->isSuperTypeOf(new IntegerType())->yes()) {
752: return TrinaryLogic::createNo();
753: }
754: }
755:
756: return TrinaryLogic::createMaybe();
757: }
758:
759: public function isString(): TrinaryLogic
760: {
761: if ($this->subtractedType !== null) {
762: if ($this->subtractedType->isSuperTypeOf(new StringType())->yes()) {
763: return TrinaryLogic::createNo();
764: }
765: }
766: return TrinaryLogic::createMaybe();
767: }
768:
769: public function isNumericString(): TrinaryLogic
770: {
771: if ($this->subtractedType !== null) {
772: $numericString = TypeCombinator::intersect(
773: new StringType(),
774: new AccessoryNumericStringType(),
775: );
776:
777: if ($this->subtractedType->isSuperTypeOf($numericString)->yes()) {
778: return TrinaryLogic::createNo();
779: }
780: }
781:
782: return TrinaryLogic::createMaybe();
783: }
784:
785: public function isNonEmptyString(): TrinaryLogic
786: {
787: if ($this->subtractedType !== null) {
788: $nonEmptyString = TypeCombinator::intersect(
789: new StringType(),
790: new AccessoryNonEmptyStringType(),
791: );
792:
793: if ($this->subtractedType->isSuperTypeOf($nonEmptyString)->yes()) {
794: return TrinaryLogic::createNo();
795: }
796: }
797:
798: return TrinaryLogic::createMaybe();
799: }
800:
801: public function isNonFalsyString(): TrinaryLogic
802: {
803: if ($this->subtractedType !== null) {
804: $nonFalsyString = TypeCombinator::intersect(
805: new StringType(),
806: new AccessoryNonFalsyStringType(),
807: );
808:
809: if ($this->subtractedType->isSuperTypeOf($nonFalsyString)->yes()) {
810: return TrinaryLogic::createNo();
811: }
812: }
813:
814: return TrinaryLogic::createMaybe();
815: }
816:
817: public function isLiteralString(): TrinaryLogic
818: {
819: if ($this->subtractedType !== null) {
820: $literalString = TypeCombinator::intersect(
821: new StringType(),
822: new AccessoryLiteralStringType(),
823: );
824:
825: if ($this->subtractedType->isSuperTypeOf($literalString)->yes()) {
826: return TrinaryLogic::createNo();
827: }
828: }
829:
830: return TrinaryLogic::createMaybe();
831: }
832:
833: public function isClassStringType(): TrinaryLogic
834: {
835: if ($this->subtractedType !== null) {
836: if ($this->subtractedType->isSuperTypeOf(new StringType())->yes()) {
837: return TrinaryLogic::createNo();
838: }
839: if ($this->subtractedType->isSuperTypeOf(new ClassStringType())->yes()) {
840: return TrinaryLogic::createNo();
841: }
842: }
843:
844: return TrinaryLogic::createMaybe();
845: }
846:
847: public function getClassStringObjectType(): Type
848: {
849: if (!$this->isClassStringType()->no()) {
850: return new ObjectWithoutClassType();
851: }
852:
853: return new ErrorType();
854: }
855:
856: public function getObjectTypeOrClassStringObjectType(): Type
857: {
858: $objectOrClass = new UnionType([
859: new ObjectWithoutClassType(),
860: new ClassStringType(),
861: ]);
862: if (!$this->isSuperTypeOf($objectOrClass)->no()) {
863: return new ObjectWithoutClassType();
864: }
865:
866: return new ErrorType();
867: }
868:
869: public function isVoid(): TrinaryLogic
870: {
871: if ($this->subtractedType !== null) {
872: if ($this->subtractedType->isSuperTypeOf(new VoidType())->yes()) {
873: return TrinaryLogic::createNo();
874: }
875: }
876:
877: return TrinaryLogic::createMaybe();
878: }
879:
880: public function isScalar(): TrinaryLogic
881: {
882: if ($this->subtractedType !== null) {
883: if ($this->subtractedType->isSuperTypeOf(new UnionType([new BooleanType(), new FloatType(), new IntegerType(), new StringType()]))->yes()) {
884: return TrinaryLogic::createNo();
885: }
886: }
887:
888: return TrinaryLogic::createMaybe();
889: }
890:
891: public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
892: {
893: return new BooleanType();
894: }
895:
896: public function tryRemove(Type $typeToRemove): ?Type
897: {
898: if ($this->isSuperTypeOf($typeToRemove)->yes()) {
899: return $this->subtract($typeToRemove);
900: }
901:
902: return null;
903: }
904:
905: public function exponentiate(Type $exponent): Type
906: {
907: return new BenevolentUnionType([
908: new FloatType(),
909: new IntegerType(),
910: ]);
911: }
912:
913: public function getFiniteTypes(): array
914: {
915: return [];
916: }
917:
918: public function toPhpDocNode(): TypeNode
919: {
920: return new IdentifierTypeNode('mixed');
921: }
922:
923: /**
924: * @param mixed[] $properties
925: */
926: public static function __set_state(array $properties): Type
927: {
928: return new self(
929: $properties['isExplicitMixed'],
930: $properties['subtractedType'] ?? null,
931: );
932: }
933:
934: }
935: