TypedClassPrinterTrait::getClassFromFqn()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
ccs 0
cts 3
cp 0
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Leonidas\Console\Library\Printer\Model\Abstracts;
6
7
use Leonidas\Library\Core\Abstracts\ConvertsCaseTrait;
8
use ReflectionClass;
9
use ReflectionFunctionAbstract;
10
use ReflectionMethod;
11
12
trait TypedClassPrinterTrait
13
{
14
    use ConvertsCaseTrait;
15
16
    protected string $type;
17
18
    protected bool $isDoingTypeMatch = false;
19
20
    public function printFromType(): string
21
    {
22
        $this->isDoingTypeMatch = true;
23
24
        $output = $this->print($this->getSignaturesFromType());
25
26
        $this->isDoingTypeMatch = false;
27
28
        return $output;
29
    }
30
31
    protected function getSignaturesFromType(): array
32
    {
33
        $defaults = $this->getDefaultSignatures();
34
        $templates = $this->getMethodTemplates();
35
36
        $signatures = [];
37
38
        foreach ($this->getTypeMethods() as $method) {
39
            $name = $method->getName();
40
41
            // output if method matches default signature
42
            if ($defaults[$name] ?? false) {
43
                $signatures[$name] = $defaults[$name];
44
45
                continue;
46
            }
47
48
            // output if method matches template signature
49
            foreach ($templates as $template => $signature) {
50
                $pattern = '/^' . str_replace('*', '\w+', $template) . '$/';
51
52
                if (!preg_match($pattern, $name = $name)) {
53
                    continue;
54
                }
55
56
                $pass = $signature['pass'] ?? false
57
                    ? $this->convertParamsToPass($template, $signature, $method)
58
                    : '';
59
60
                $signatures[$name] = [
61
                    'take' => $this->stringifyParams($method),
62
                    'give' => $signature['give'] ?? null,
63
                    'call' => $signature['call'],
64
                    'pass' => $pass,
65
                ];
66
67
                continue 2;
68
            }
69
        }
70
71
        return $signatures;
72
    }
73
74
    protected function convertParamsToPass(string $template, array $signature, ReflectionMethod $method): string
75
    {
76
        $base = str_replace(explode('*', $template), '', $method->getName());
77
78
        $key = $this->convert($base)->toSnake();
79
        $var = $this->convert($base)->toCamel();
80
81
        return str_replace(
82
            ['#*', '$*'],
83
            ["'{$key}'", "\${$var}"],
84
            $signature['pass']
85
        );
86
    }
87
88
    protected function stringifyParams(ReflectionFunctionAbstract $method): string
89
    {
90
        $params = $method->getParameters();
91
92
        $take = [];
93
94
        foreach ($params as $param) {
95
            $structure = '';
96
97
            if ($param->allowsNull()) {
98
                $structure .= '?';
99
            }
100
101
            if ($param->hasType()) {
102
                $type = $param->getType()->getName(); // @phpstan-ignore-line
0 ignored issues
show
Bug introduced by
The method getName() does not exist on ReflectionType. It seems like you code against a sub-type of ReflectionType such as ReflectionNamedType. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

102
                $type = $param->getType()->/** @scrutinizer ignore-call */ getName(); // @phpstan-ignore-line
Loading history...
103
104
                if ($this->typeIsConstruct($type)) {
105
                    $this->addImport($type);
106
                    $type = $this->getClassFromFqn($type);
107
                }
108
109
                $structure .= $type . ' ';
110
            }
111
112
            if ($param->isPassedByReference()) {
113
                $structure .= '&';
114
            }
115
116
            if ($param->isVariadic()) {
117
                $structure .= '...';
118
            }
119
120
            $structure .= '$' . $param->getName();
121
122
            if ($param->isDefaultValueAvailable()) {
123
                $structure .= ' = ' . (string) $param->getDefaultValue();
124
            }
125
126
            $take[] = $structure;
127
        }
128
129
        return implode(', ', $take);
130
    }
131
132
    protected function typeIsConstruct(string $type): bool
133
    {
134
        return class_exists($type)
135
            || interface_exists($type)
136
            || enum_exists($type);
137
    }
138
139
    protected function getClassFromFqn(string $fqn): string
140
    {
141
        $parts = explode('\\', $fqn);
142
143
        return end($parts);
144
    }
145
146
    protected function matchTraitsToType(array $traits, array $map)
147
    {
148
        $extensions = array_values(class_implements($this->type));
149
150
        return array_filter(
151
            $traits,
152
            fn ($partial) => in_array($map[$partial], $extensions)
153
        );
154
    }
155
156
    /**
157
     * @return ReflectionMethod[]
158
     */
159
    protected function getTypeMethods(): array
160
    {
161
        $reflection = new ReflectionClass($this->type);
162
163
        return $reflection->getMethods();
164
    }
165
166
    protected function isDoingTypeMatch(): bool
167
    {
168
        return $this->isDoingTypeMatch;
169
    }
170
171
    protected function getMethodTemplates(): array
172
    {
173
        return [];
174
    }
175
176
    abstract protected function addImport(string $import): void;
177
178
    abstract protected function print(array $methods): string;
179
180
    abstract protected function getDefaultSignatures(): array;
181
}
182