1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* This file is part of Railt package. |
4
|
|
|
* |
5
|
|
|
* For the full copyright and license information, please view the LICENSE |
6
|
|
|
* file that was distributed with this source code. |
7
|
|
|
*/ |
8
|
|
|
declare(strict_types=1); |
9
|
|
|
|
10
|
|
|
namespace Railt\SDL\IR; |
11
|
|
|
|
12
|
|
|
use Railt\SDL\IR\Type\Name; |
13
|
|
|
use Railt\SDL\IR\Type\TypeConstructors; |
14
|
|
|
use Railt\SDL\IR\Type\InternalTypes; |
15
|
|
|
use Railt\SDL\IR\Type\TypeInterface; |
16
|
|
|
use Railt\SDL\IR\Type\TypeNameInterface; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Class Type |
20
|
|
|
*/ |
21
|
|
|
class Type implements TypeInterface |
22
|
|
|
{ |
23
|
|
|
use InternalTypes; |
24
|
|
|
use TypeConstructors; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @var bool |
28
|
|
|
*/ |
29
|
|
|
private static $booted = false; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var TypeNameInterface |
33
|
|
|
*/ |
34
|
|
|
protected $type; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var TypeInterface |
38
|
|
|
*/ |
39
|
|
|
protected $of; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @var bool|null |
43
|
|
|
*/ |
44
|
|
|
private $inputable; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @var bool|null |
48
|
|
|
*/ |
49
|
|
|
private $returnable; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @var string |
53
|
|
|
*/ |
54
|
|
|
private $hash; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* Type constructor. |
58
|
|
|
* @param string|iterable|TypeNameInterface $name |
59
|
|
|
* @param TypeInterface|null $of |
60
|
|
|
*/ |
61
|
|
|
protected function __construct($name, TypeInterface $of = null) |
62
|
|
|
{ |
63
|
|
|
$this->type = Name::new($name); |
64
|
|
|
$this->of = $this->resolveTypeOf($this->type, $of); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* @param TypeNameInterface $type |
69
|
|
|
* @param null|TypeInterface $of |
70
|
|
|
* @return TypeInterface|static |
71
|
|
|
*/ |
72
|
|
|
private function resolveTypeOf(TypeNameInterface $type, ?TypeInterface $of): TypeInterface |
73
|
|
|
{ |
74
|
|
|
if ($of === null) { |
75
|
|
|
$any = Name::new(self::ROOT_TYPE); |
76
|
|
|
|
77
|
|
|
return $type->is($any) ? $this : self::new($any); |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
return $of; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @return void |
85
|
|
|
*/ |
86
|
51 |
|
private static function bootIfNotBooted(): void |
87
|
|
|
{ |
88
|
51 |
|
if (self::$booted === false) { |
89
|
|
|
self::$booted = true; |
90
|
|
|
|
91
|
|
|
self::bootInternalTypes(); |
92
|
|
|
} |
93
|
51 |
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* @param string|iterable|TypeNameInterface $name |
97
|
|
|
* @param TypeInterface|null $of |
98
|
|
|
* @return TypeInterface|static |
99
|
|
|
*/ |
100
|
51 |
|
public static function new($name, TypeInterface $of = null): TypeInterface |
101
|
|
|
{ |
102
|
51 |
|
self::bootIfNotBooted(); |
103
|
|
|
|
104
|
51 |
|
$fqn = Name::new($name)->getFullyQualifiedName(); |
105
|
|
|
|
106
|
51 |
|
return self::getInternalType($fqn, $of, function () use ($name, $of): TypeInterface { |
107
|
|
|
return new static($name, $of); |
108
|
51 |
|
}); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* @param TypeInterface $of |
113
|
|
|
* @return TypeInterface|static |
114
|
|
|
*/ |
115
|
|
|
public function of(TypeInterface $of): TypeInterface |
116
|
|
|
{ |
117
|
|
|
\assert(! $this->isInternal(), 'Can not change inheritance logic of internal types'); |
118
|
|
|
|
119
|
|
|
return static::new($this->type, $of); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* {@inheritdoc} |
124
|
|
|
*/ |
125
|
50 |
|
public function getParent(): TypeInterface |
126
|
|
|
{ |
127
|
50 |
|
return $this->of; |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* {@inheritdoc} |
132
|
|
|
*/ |
133
|
51 |
|
public function typeOf(TypeInterface $type): bool |
134
|
|
|
{ |
135
|
51 |
|
foreach ($this->getInheritanceSequence($this) as $parent) { |
136
|
51 |
|
if ($parent->is($type)) { |
137
|
51 |
|
return true; |
138
|
|
|
} |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
return false; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* @param TypeInterface $type |
146
|
|
|
* @return \Generator|TypeInterface[] |
147
|
|
|
*/ |
148
|
51 |
|
private function getInheritanceSequence(TypeInterface $type): \Generator |
149
|
|
|
{ |
150
|
51 |
|
yield $type; |
151
|
|
|
|
152
|
50 |
|
if (! $type->getName()->is(static::ROOT_TYPE)) { |
153
|
50 |
|
yield from $this->getInheritanceSequence($type->getParent()); |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* {@inheritdoc} |
159
|
|
|
*/ |
160
|
|
|
public function isInputable(): bool |
161
|
|
|
{ |
162
|
|
|
if ($this->inputable === null) { |
163
|
|
|
if ($this->isBuiltin() && ! \in_array($this->fqn(), static::ALLOWS_TO_INPUT, true)) { |
164
|
|
|
return $this->inputable = false; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
if ($this->is(self::any())) { |
|
|
|
|
168
|
|
|
return $this->inputable = true; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
$this->inputable = $this->of->isInputable(); |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
return $this->inputable; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* {@inheritdoc} |
179
|
|
|
*/ |
180
|
51 |
|
public function isBuiltin(): bool |
181
|
|
|
{ |
182
|
51 |
|
$fqn = $this->fqn(); |
183
|
|
|
|
184
|
51 |
|
return \in_array($fqn, static::INDEPENDENT_TYPES, true) |
185
|
21 |
|
|| \in_array($fqn, static::WRAPPING_TYPES, true) |
186
|
51 |
|
|| \in_array($fqn, static::DEPENDENT_TYPES, true) |
187
|
|
|
; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* @return string |
192
|
|
|
*/ |
193
|
68 |
|
private function fqn(): string |
194
|
|
|
{ |
195
|
68 |
|
return $this->type->getFullyQualifiedName(); |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* {@inheritdoc} |
200
|
|
|
*/ |
201
|
51 |
|
public function is(TypeInterface $is): bool |
202
|
|
|
{ |
203
|
51 |
|
return $is->getHash() === $this->getHash(); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* {@inheritdoc} |
208
|
|
|
*/ |
209
|
152 |
|
public function getName(): TypeNameInterface |
210
|
|
|
{ |
211
|
152 |
|
return $this->type; |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
/** |
215
|
|
|
* {@inheritdoc} |
216
|
|
|
*/ |
217
|
|
|
public function isReturnable(): bool |
218
|
|
|
{ |
219
|
|
|
if ($this->returnable === null) { |
220
|
|
|
if ($this->isBuiltin() && ! \in_array($this->fqn(), static::ALLOWS_TO_OUTPUT, true)) { |
221
|
|
|
return $this->returnable = false; |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
if ($this->is(self::any())) { |
|
|
|
|
225
|
|
|
return $this->returnable = true; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
$this->returnable = $this->of->isReturnable(); |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
return $this->returnable; |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
/** |
235
|
|
|
* @return array |
236
|
|
|
*/ |
237
|
|
|
public function __debugInfo(): array |
238
|
|
|
{ |
239
|
|
|
return [ |
240
|
|
|
'type' => $this->fqn(), |
241
|
|
|
'of' => $this->of, |
242
|
|
|
]; |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* @return string |
247
|
|
|
*/ |
248
|
|
|
public function __toString(): string |
249
|
|
|
{ |
250
|
|
|
if ($this->isInternal()) { |
251
|
|
|
return $this->fqn(); |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
return \sprintf('%s<%s>', $this->fqn(), $this->of); |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* @return bool |
259
|
|
|
*/ |
260
|
|
|
private function isInternal(): bool |
261
|
|
|
{ |
262
|
|
|
return $this->isBuiltin() || \in_array($this->fqn(), self::RUNTIME_TYPES, true); |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
/** |
266
|
|
|
* @return string |
267
|
|
|
*/ |
268
|
51 |
|
public function getHash(): string |
269
|
|
|
{ |
270
|
51 |
|
if ($this->hash === null) { |
271
|
17 |
|
$this->hash = $this->fqn() === self::ROOT_TYPE |
272
|
|
|
? \sha1($this->fqn()) |
273
|
17 |
|
: \sha1($this->fqn() . ':' . $this->of->getHash()); |
274
|
|
|
} |
275
|
|
|
|
276
|
51 |
|
return $this->hash; |
277
|
|
|
} |
278
|
|
|
} |
279
|
|
|
|
This check looks at variables that are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.