1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type\Enum;
4:
5: use PHPStan\Reflection\ClassMemberAccessAnswerer;
6: use PHPStan\Reflection\ClassReflection;
7: use PHPStan\Reflection\Php\EnumPropertyReflection;
8: use PHPStan\Reflection\PropertyReflection;
9: use PHPStan\ShouldNotHappenException;
10: use PHPStan\TrinaryLogic;
11: use PHPStan\Type\CompoundType;
12: use PHPStan\Type\Constant\ConstantStringType;
13: use PHPStan\Type\GeneralizePrecision;
14: use PHPStan\Type\ObjectType;
15: use PHPStan\Type\Type;
16: use PHPStan\Type\VerbosityLevel;
17: use function sprintf;
18:
19: /** @api */
20: class EnumCaseObjectType extends ObjectType
21: {
22:
23: /** @api */
24: public function __construct(
25: string $className,
26: private string $enumCaseName,
27: ?ClassReflection $classReflection = null,
28: )
29: {
30: parent::__construct($className, null, $classReflection);
31: }
32:
33: public function getEnumCaseName(): string
34: {
35: return $this->enumCaseName;
36: }
37:
38: public function describe(VerbosityLevel $level): string
39: {
40: $parent = parent::describe($level);
41:
42: return sprintf('%s::%s', $parent, $this->enumCaseName);
43: }
44:
45: public function equals(Type $type): bool
46: {
47: if (!$type instanceof self) {
48: return false;
49: }
50:
51: return $this->getClassName() === $type->getClassName()
52: && $this->enumCaseName === $type->enumCaseName;
53: }
54:
55: public function accepts(Type $type, bool $strictTypes): TrinaryLogic
56: {
57: return $this->isSuperTypeOf($type);
58: }
59:
60: public function isSuperTypeOf(Type $type): TrinaryLogic
61: {
62: if ($type instanceof self) {
63: return TrinaryLogic::createFromBoolean(
64: $this->getClassName() === $type->getClassName()
65: && $this->enumCaseName === $type->enumCaseName,
66: );
67: }
68:
69: if ($type instanceof CompoundType) {
70: return $type->isSubTypeOf($this);
71: }
72:
73: $parent = new parent($this->getClassName(), $this->getSubtractedType(), $this->getClassReflection());
74:
75: return $parent->isSuperTypeOf($type)->and(TrinaryLogic::createMaybe());
76: }
77:
78: public function subtract(Type $type): Type
79: {
80: return $this;
81: }
82:
83: public function getTypeWithoutSubtractedType(): Type
84: {
85: return $this;
86: }
87:
88: public function changeSubtractedType(?Type $subtractedType): Type
89: {
90: return $this;
91: }
92:
93: public function getSubtractedType(): ?Type
94: {
95: return null;
96: }
97:
98: public function tryRemove(Type $typeToRemove): ?Type
99: {
100: if ($this->isSuperTypeOf($typeToRemove)->yes()) {
101: return $this->subtract($typeToRemove);
102: }
103:
104: return null;
105: }
106:
107: public function getProperty(string $propertyName, ClassMemberAccessAnswerer $scope): PropertyReflection
108: {
109: $classReflection = $this->getClassReflection();
110: if ($classReflection === null) {
111: return parent::getProperty($propertyName, $scope);
112:
113: }
114: if ($propertyName === 'name') {
115: return new EnumPropertyReflection($classReflection, new ConstantStringType($this->enumCaseName));
116: }
117:
118: if ($classReflection->isBackedEnum() && $propertyName === 'value') {
119: if ($classReflection->hasEnumCase($this->enumCaseName)) {
120: $enumCase = $classReflection->getEnumCase($this->enumCaseName);
121: $valueType = $enumCase->getBackingValueType();
122: if ($valueType === null) {
123: throw new ShouldNotHappenException();
124: }
125:
126: return new EnumPropertyReflection($classReflection, $valueType);
127: }
128: }
129:
130: return parent::getProperty($propertyName, $scope);
131: }
132:
133: public function generalize(GeneralizePrecision $precision): Type
134: {
135: return new parent($this->getClassName(), null, $this->getClassReflection());
136: }
137:
138: public function isSmallerThan(Type $otherType): TrinaryLogic
139: {
140: return TrinaryLogic::createNo();
141: }
142:
143: public function isSmallerThanOrEqual(Type $otherType): TrinaryLogic
144: {
145: return TrinaryLogic::createNo();
146: }
147:
148: /**
149: * @param mixed[] $properties
150: */
151: public static function __set_state(array $properties): Type
152: {
153: return new self($properties['className'], $properties['enumCaseName'], null);
154: }
155:
156: }
157: