1: | <?php declare(strict_types = 1); |
2: | |
3: | namespace PHPStan\Type; |
4: | |
5: | use PHPStan\TrinaryLogic; |
6: | use PHPStan\Type\Generic\TemplateTypeVariance; |
7: | use PHPStan\Type\Traits\LateResolvableTypeTrait; |
8: | use PHPStan\Type\Traits\NonGeneralizableTypeTrait; |
9: | use function array_merge; |
10: | use function sprintf; |
11: | |
12: | |
13: | final class ConditionalType implements CompoundType, LateResolvableType |
14: | { |
15: | |
16: | use LateResolvableTypeTrait; |
17: | use NonGeneralizableTypeTrait; |
18: | |
19: | public function __construct( |
20: | private Type $subject, |
21: | private Type $target, |
22: | private Type $if, |
23: | private Type $else, |
24: | private bool $negated, |
25: | ) |
26: | { |
27: | } |
28: | |
29: | public function getSubject(): Type |
30: | { |
31: | return $this->subject; |
32: | } |
33: | |
34: | public function getTarget(): Type |
35: | { |
36: | return $this->target; |
37: | } |
38: | |
39: | public function getIf(): Type |
40: | { |
41: | return $this->if; |
42: | } |
43: | |
44: | public function getElse(): Type |
45: | { |
46: | return $this->else; |
47: | } |
48: | |
49: | public function isNegated(): bool |
50: | { |
51: | return $this->negated; |
52: | } |
53: | |
54: | public function isSuperTypeOf(Type $type): TrinaryLogic |
55: | { |
56: | if ($type instanceof self) { |
57: | return $this->if->isSuperTypeOf($type->if) |
58: | ->and($this->else->isSuperTypeOf($type->else)); |
59: | } |
60: | |
61: | return $this->isSuperTypeOfDefault($type); |
62: | } |
63: | |
64: | public function getReferencedClasses(): array |
65: | { |
66: | return array_merge( |
67: | $this->subject->getReferencedClasses(), |
68: | $this->target->getReferencedClasses(), |
69: | $this->if->getReferencedClasses(), |
70: | $this->else->getReferencedClasses(), |
71: | ); |
72: | } |
73: | |
74: | public function getReferencedTemplateTypes(TemplateTypeVariance $positionVariance): array |
75: | { |
76: | return array_merge( |
77: | $this->subject->getReferencedTemplateTypes($positionVariance), |
78: | $this->target->getReferencedTemplateTypes($positionVariance), |
79: | $this->if->getReferencedTemplateTypes($positionVariance), |
80: | $this->else->getReferencedTemplateTypes($positionVariance), |
81: | ); |
82: | } |
83: | |
84: | public function equals(Type $type): bool |
85: | { |
86: | return $type instanceof self |
87: | && $this->subject->equals($type->subject) |
88: | && $this->target->equals($type->target) |
89: | && $this->if->equals($type->if) |
90: | && $this->else->equals($type->else); |
91: | } |
92: | |
93: | public function describe(VerbosityLevel $level): string |
94: | { |
95: | return sprintf( |
96: | '(%s %s %s ? %s : %s)', |
97: | $this->subject->describe($level), |
98: | $this->negated ? 'is not' : 'is', |
99: | $this->target->describe($level), |
100: | $this->if->describe($level), |
101: | $this->else->describe($level), |
102: | ); |
103: | } |
104: | |
105: | public function isResolvable(): bool |
106: | { |
107: | return !TypeUtils::containsTemplateType($this->subject) && !TypeUtils::containsTemplateType($this->target); |
108: | } |
109: | |
110: | protected function getResult(): Type |
111: | { |
112: | $isSuperType = $this->target->isSuperTypeOf($this->subject); |
113: | |
114: | if ($isSuperType->yes()) { |
115: | return !$this->negated ? $this->if : $this->else; |
116: | } |
117: | |
118: | if ($isSuperType->no()) { |
119: | return !$this->negated ? $this->else : $this->if; |
120: | } |
121: | |
122: | return TypeCombinator::union($this->if, $this->else); |
123: | } |
124: | |
125: | public function traverse(callable $cb): Type |
126: | { |
127: | $subject = $cb($this->subject); |
128: | $target = $cb($this->target); |
129: | $if = $cb($this->if); |
130: | $else = $cb($this->else); |
131: | |
132: | if ($this->subject === $subject && $this->target === $target && $this->if === $if && $this->else === $else) { |
133: | return $this; |
134: | } |
135: | |
136: | return new self($subject, $target, $if, $else, $this->negated); |
137: | } |
138: | |
139: | |
140: | |
141: | |
142: | public static function __set_state(array $properties): Type |
143: | { |
144: | return new self( |
145: | $properties['subject'], |
146: | $properties['target'], |
147: | $properties['if'], |
148: | $properties['else'], |
149: | $properties['negated'], |
150: | ); |
151: | } |
152: | |
153: | } |
154: | |