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