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