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: /** @api */
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: