Passed
Pull Request — master (#30)
by
unknown
46:28 queued 21:27
created

TypeDeclaration::references()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 13
nc 5
nop 0
dl 0
loc 20
ccs 13
cts 13
cp 1
crap 6
rs 9.2222
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
/**
3
 * This source file is subject to the license that is bundled with this package in the file LICENSE.
4
 */
5
6
namespace PhUml\Code\Variables;
7
8
use PhUml\Code\Name;
9
use Stringable;
10
11
/**
12
 * It represents a variable's type declaration
13
 */
14
final class TypeDeclaration implements Stringable
15
{
16
    /** @var string[] All valid types for PHP 8.1, pseudo-types, and aliases */
17
    private const BUILT_IN_TYPES = [
18
        // https://www.php.net/manual/en/language.types.declarations.php#language.types.declarations.base
19
        'int', 'bool', 'string', 'array', 'float', 'callable', 'iterable', 'mixed', 'object',
20
        // https://www.php.net/manual/en/language.types.declarations.php#language.types.declarations.union.nullable
21
        'null',
22
        // https://www.php.net/manual/en/language.types.declarations.php#language.types.declarations.return-only
23
        'void', 'never',
24
        // pseudo-types
25
        'resource',
26
        // aliases
27
        'number', 'boolean', 'integer', 'double',
28
    ];
29
30
    /** @var Name[] */
31
    private readonly array $names;
32
33 95
    public static function absent(): TypeDeclaration
34
    {
35 95
        return new TypeDeclaration();
36
    }
37
38 104
    public static function from(?string $type): TypeDeclaration
39
    {
40 104
        return new TypeDeclaration($type === null ? [] : [new Name($type)]);
41
    }
42
43 23
    public static function fromNullable(string $type): TypeDeclaration
44
    {
45 23
        return new TypeDeclaration([new Name($type)], isNullable: true);
46
    }
47
48
    /**
49
     * A composite type can be either a union or an intersection type
50
     *
51
     * @param string[] $types
52
     * @link https://www.php.net/manual/en/language.types.declarations.php#language.types.declarations.composite.union Union types documentation
53
     * @link https://www.php.net/manual/en/language.types.declarations.php#language.types.declarations.composite.intersection Intersection types documentation
54
     */
55 35
    public static function fromCompositeType(array $types, CompositeType $compositeType): TypeDeclaration
56
    {
57 35
        return new TypeDeclaration(
58 35
            array_map(static fn (string $type) => new Name($type), $types),
59
            compositeType: $compositeType
60
        );
61
    }
62
63
    /** @param Name[] $names */
64 157
    private function __construct(
65
        array $names = [],
66
        private readonly bool $isNullable = false,
67
        private readonly CompositeType $compositeType = CompositeType::NONE
68
    ) {
69 157
        $this->names = $names;
0 ignored issues
show
Bug introduced by
The property names is declared read-only in PhUml\Code\Variables\TypeDeclaration.
Loading history...
70
    }
71
72 71
    public function isPresent(): bool
73
    {
74 71
        return $this->names !== [];
75
    }
76
77
    /** @return Name[] */
78 27
    public function references(): array
79
    {
80 27
        if (! $this->isPresent()) {
81 11
            return [];
82
        }
83 26
        if ($this->isBuiltIn()) {
84 10
            return [];
85
        }
86 23
        if ($this->isRegularType()) {
87 21
            return [$this->isArray() ? new Name($this->removeArraySuffix()) : $this->names[0]];
88
        }
89
90 4
        $typesFromUnion = array_map(static fn (Name $name) => TypeDeclaration::from($name->fullName()), $this->names);
91 4
        $references = array_filter($typesFromUnion, static fn (TypeDeclaration $type) => ! $type->isBuiltIn());
92
93 4
        return array_map(
94 4
            static fn (TypeDeclaration $reference) => $reference->isArray()
95 2
                ? new Name($reference->removeArraySuffix())
96 4
                : $reference->names[0],
97
            $references
98
        );
99
    }
100
101
    /**
102
     * It helps building the relationships between classes/interfaces since built-in
103
     * types are not part of a UML class diagram
104
     */
105 44
    public function isBuiltIn(): bool
106
    {
107 44
        if (! $this->isRegularType()) {
108 6
            return false;
109
        }
110 43
        $type = (string) $this->names[0];
111 43
        if ($this->isArray()) {
112 8
            $type = $this->removeArraySuffix();
113
        }
114
115 43
        return in_array($type, self::BUILT_IN_TYPES, true);
116
    }
117
118 8
    private function removeArraySuffix(): string
119
    {
120 8
        return substr($this->names[0]->fullName(), 0, -2);
121
    }
122
123 32
    public function isBuiltInArray(): bool
124
    {
125 32
        if ($this->isRegularType()) {
126 29
            return (string) $this->names[0] === 'array';
127
        }
128 17
        return false;
129
    }
130
131 43
    private function isArray(): bool
132
    {
133 43
        return str_ends_with($this->names[0]->fullName(), '[]');
134
    }
135
136 1
    public function isNullable(): bool
137
    {
138 1
        return $this->isNullable;
139
    }
140
141 72
    private function isRegularType(): bool
142
    {
143 72
        return count($this->names) === 1;
144
    }
145
146 49
    public function __toString(): string
147
    {
148 49
        return ($this->isNullable ? '?' : '') . implode($this->compositeType->value, $this->names);
149
    }
150
}
151