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: /** @api */
13: final class ConditionalTypeForParameter implements CompoundType, LateResolvableType
14: {
15:
16: use LateResolvableTypeTrait;
17: use NonGeneralizableTypeTrait;
18:
19: public function __construct(
20: private string $parameterName,
21: private Type $target,
22: private Type $if,
23: private Type $else,
24: private bool $negated,
25: )
26: {
27: }
28:
29: public function getParameterName(): string
30: {
31: return $this->parameterName;
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 changeParameterName(string $parameterName): self
55: {
56: return new self(
57: $parameterName,
58: $this->target,
59: $this->if,
60: $this->else,
61: $this->negated,
62: );
63: }
64:
65: public function toConditional(Type $subject): Type
66: {
67: return new ConditionalType(
68: $subject,
69: $this->target,
70: $this->if,
71: $this->else,
72: $this->negated,
73: );
74: }
75:
76: public function isSuperTypeOf(Type $type): TrinaryLogic
77: {
78: if ($type instanceof self) {
79: return $this->if->isSuperTypeOf($type->if)
80: ->and($this->else->isSuperTypeOf($type->else));
81: }
82:
83: return $this->isSuperTypeOfDefault($type);
84: }
85:
86: public function getReferencedClasses(): array
87: {
88: return array_merge(
89: $this->target->getReferencedClasses(),
90: $this->if->getReferencedClasses(),
91: $this->else->getReferencedClasses(),
92: );
93: }
94:
95: public function getReferencedTemplateTypes(TemplateTypeVariance $positionVariance): array
96: {
97: return array_merge(
98: $this->target->getReferencedTemplateTypes($positionVariance),
99: $this->if->getReferencedTemplateTypes($positionVariance),
100: $this->else->getReferencedTemplateTypes($positionVariance),
101: );
102: }
103:
104: public function equals(Type $type): bool
105: {
106: return $type instanceof self
107: && $this->parameterName === $type->parameterName
108: && $this->target->equals($type->target)
109: && $this->if->equals($type->if)
110: && $this->else->equals($type->else);
111: }
112:
113: public function describe(VerbosityLevel $level): string
114: {
115: return sprintf(
116: '(%s %s %s ? %s : %s)',
117: $this->parameterName,
118: $this->negated ? 'is not' : 'is',
119: $this->target->describe($level),
120: $this->if->describe($level),
121: $this->else->describe($level),
122: );
123: }
124:
125: public function isResolvable(): bool
126: {
127: return false;
128: }
129:
130: protected function getResult(): Type
131: {
132: return TypeCombinator::union($this->if, $this->else);
133: }
134:
135: /**
136: * @param callable(Type): Type $cb
137: */
138: public function traverse(callable $cb): Type
139: {
140: $target = $cb($this->target);
141: $if = $cb($this->if);
142: $else = $cb($this->else);
143:
144: if ($this->target === $target && $this->if === $if && $this->else === $else) {
145: return $this;
146: }
147:
148: return new ConditionalTypeForParameter($this->parameterName, $target, $if, $else, $this->negated);
149: }
150:
151: /**
152: * @param mixed[] $properties
153: */
154: public static function __set_state(array $properties): Type
155: {
156: return new self(
157: $properties['parameterName'],
158: $properties['target'],
159: $properties['if'],
160: $properties['else'],
161: $properties['negated'],
162: );
163: }
164:
165: }
166: