Passed
Push — master ( 3491f1...e1ceb1 )
by Chris
40:32
created

TypedClassPrinterTrait::getSignaturesFromType()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 41
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 22
c 1
b 0
f 0
nc 6
nop 0
dl 0
loc 41
ccs 0
cts 22
cp 0
crap 42
rs 8.9457
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 ReflectionMethod;
10
11
trait TypedClassPrinterTrait
12
{
13
    use ConvertsCaseTrait;
14
15
    protected string $type;
16
17
    protected bool $isDoingTypeMatch = false;
18
19
    public function printFromType(): string
20
    {
21
        $this->isDoingTypeMatch = true;
22
23
        $output = $this->print($this->getSignaturesFromType());
24
25
        $this->isDoingTypeMatch = false;
26
27
        return $output;
28
    }
29
30
    protected function getSignaturesFromType(): array
31
    {
32
        $defaults = $this->getDefaultSignatures();
33
        $templates = $this->getMethodTemplates();
34
35
        $signatures = [];
36
37
        foreach ($this->getTypeMethods() as $method) {
38
            $name = $method->getName();
39
40
            // output if method matches default signature
41
            if ($defaults[$name] ?? false) {
42
                $signatures[$name] = $defaults[$name];
43
44
                continue;
45
            }
46
47
            // output if method matches template signature
48
            foreach ($templates as $template => $signature) {
49
                $pattern = '/^' . str_replace('*', '\w+', $template) . '$/';
50
51
                if (!preg_match($pattern, $name = $name)) {
52
                    continue;
53
                }
54
55
                $pass = $signature['pass'] ?? false
56
                    ? $this->convertParamsToPass($template, $signature, $method)
57
                    : '';
58
59
                $signatures[$name] = [
60
                    'take' => $this->convertParamsToTake($method),
61
                    'give' => $signature['give'] ?? null,
62
                    'call' => $signature['call'],
63
                    'pass' => $pass,
64
                ];
65
66
                continue 2;
67
            }
68
        }
69
70
        return $signatures;
71
    }
72
73
    protected function convertParamsToPass(string $template, array $signature, ReflectionMethod $method): string
74
    {
75
        $base = str_replace(explode('*', $template), '', $method->getName());
76
77
        $key = $this->convert($base)->toSnake();
78
        $var = $this->convert($base)->toCamel();
79
80
        return str_replace(
81
            ['#*', '$*'],
82
            ["'{$key}'", "\${$var}"],
83
            $signature['pass']
84
        );
85
    }
86
87
    protected function convertParamsToTake(ReflectionMethod $method): string
88
    {
89
        $params = $method->getParameters();
90
91
        $take = [];
92
93
        foreach ($params as $param) {
94
            $structure = '';
95
96
            if ($param->allowsNull()) {
97
                $structure .= '?';
98
            }
99
100
            if ($param->hasType()) {
101
                $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

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