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