1: | <?php declare(strict_types = 1); |
2: | |
3: | namespace PHPStan\Type\Generic; |
4: | |
5: | use PHPStan\ShouldNotHappenException; |
6: | use PHPStan\TrinaryLogic; |
7: | use PHPStan\Type\BenevolentUnionType; |
8: | use PHPStan\Type\MixedType; |
9: | use PHPStan\Type\NeverType; |
10: | use PHPStan\Type\Type; |
11: | |
12: | |
13: | class TemplateTypeVariance |
14: | { |
15: | |
16: | private const INVARIANT = 1; |
17: | private const COVARIANT = 2; |
18: | private const CONTRAVARIANT = 3; |
19: | private const STATIC = 4; |
20: | |
21: | |
22: | private static array $registry; |
23: | |
24: | private function __construct(private int $value) |
25: | { |
26: | } |
27: | |
28: | private static function create(int $value): self |
29: | { |
30: | self::$registry[$value] ??= new self($value); |
31: | return self::$registry[$value]; |
32: | } |
33: | |
34: | public static function createInvariant(): self |
35: | { |
36: | return self::create(self::INVARIANT); |
37: | } |
38: | |
39: | public static function createCovariant(): self |
40: | { |
41: | return self::create(self::COVARIANT); |
42: | } |
43: | |
44: | public static function createContravariant(): self |
45: | { |
46: | return self::create(self::CONTRAVARIANT); |
47: | } |
48: | |
49: | public static function createStatic(): self |
50: | { |
51: | return self::create(self::STATIC); |
52: | } |
53: | |
54: | public function invariant(): bool |
55: | { |
56: | return $this->value === self::INVARIANT; |
57: | } |
58: | |
59: | public function covariant(): bool |
60: | { |
61: | return $this->value === self::COVARIANT; |
62: | } |
63: | |
64: | public function contravariant(): bool |
65: | { |
66: | return $this->value === self::CONTRAVARIANT; |
67: | } |
68: | |
69: | public function static(): bool |
70: | { |
71: | return $this->value === self::STATIC; |
72: | } |
73: | |
74: | public function compose(self $other): self |
75: | { |
76: | if ($this->contravariant()) { |
77: | if ($other->contravariant()) { |
78: | return self::createCovariant(); |
79: | } |
80: | if ($other->covariant()) { |
81: | return self::createContravariant(); |
82: | } |
83: | return self::createInvariant(); |
84: | } |
85: | |
86: | if ($this->covariant()) { |
87: | if ($other->contravariant()) { |
88: | return self::createCovariant(); |
89: | } |
90: | if ($other->covariant()) { |
91: | return self::createCovariant(); |
92: | } |
93: | return self::createInvariant(); |
94: | } |
95: | |
96: | return $other; |
97: | } |
98: | |
99: | public function isValidVariance(Type $a, Type $b): TrinaryLogic |
100: | { |
101: | if ($b instanceof NeverType) { |
102: | return TrinaryLogic::createYes(); |
103: | } |
104: | |
105: | if ($a instanceof MixedType && !$a instanceof TemplateType) { |
106: | return TrinaryLogic::createYes(); |
107: | } |
108: | |
109: | if ($a instanceof BenevolentUnionType) { |
110: | if (!$a->isSuperTypeOf($b)->no()) { |
111: | return TrinaryLogic::createYes(); |
112: | } |
113: | } |
114: | |
115: | if ($b instanceof BenevolentUnionType) { |
116: | if (!$b->isSuperTypeOf($a)->no()) { |
117: | return TrinaryLogic::createYes(); |
118: | } |
119: | } |
120: | |
121: | if ($b instanceof MixedType && !$b instanceof TemplateType) { |
122: | return TrinaryLogic::createYes(); |
123: | } |
124: | |
125: | if ($this->invariant()) { |
126: | return TrinaryLogic::createFromBoolean($a->equals($b)); |
127: | } |
128: | |
129: | if ($this->covariant()) { |
130: | return $a->isSuperTypeOf($b); |
131: | } |
132: | |
133: | if ($this->contravariant()) { |
134: | return $b->isSuperTypeOf($a); |
135: | } |
136: | |
137: | throw new ShouldNotHappenException(); |
138: | } |
139: | |
140: | public function equals(self $other): bool |
141: | { |
142: | return $other->value === $this->value; |
143: | } |
144: | |
145: | public function validPosition(self $other): bool |
146: | { |
147: | return $other->value === $this->value |
148: | || $other->invariant() |
149: | || $this->static(); |
150: | } |
151: | |
152: | public function describe(): string |
153: | { |
154: | switch ($this->value) { |
155: | case self::INVARIANT: |
156: | return 'invariant'; |
157: | case self::COVARIANT: |
158: | return 'covariant'; |
159: | case self::CONTRAVARIANT: |
160: | return 'contravariant'; |
161: | case self::STATIC: |
162: | return 'static'; |
163: | } |
164: | |
165: | throw new ShouldNotHappenException(); |
166: | } |
167: | |
168: | |
169: | |
170: | |
171: | public static function __set_state(array $properties): self |
172: | { |
173: | return new self($properties['value']); |
174: | } |
175: | |
176: | } |
177: | |