Test Failed
Push — master ( b7ab7b...94ee03 )
by Kirill
02:59
created

Type::getInheritanceSequence()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 8
ccs 4
cts 5
cp 0.8
crap 2.032
rs 10
c 0
b 0
f 0
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())) {
0 ignored issues
show
Bug introduced by
It seems like self::any() targeting Railt\SDL\IR\Type\TypeCo...TypeConstructors::any() can also be of type object<Railt\SDL\IR\Type...uiltinTypeConstructors>; however, Railt\SDL\IR\Type::is() does only seem to accept object<Railt\SDL\IR\Type\TypeInterface>, maybe add an additional type check?

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.

Loading history...
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())) {
0 ignored issues
show
Bug introduced by
It seems like self::any() targeting Railt\SDL\IR\Type\TypeCo...TypeConstructors::any() can also be of type object<Railt\SDL\IR\Type...uiltinTypeConstructors>; however, Railt\SDL\IR\Type::is() does only seem to accept object<Railt\SDL\IR\Type\TypeInterface>, maybe add an additional type check?

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.

Loading history...
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