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