Passed
Pull Request — master (#21)
by
unknown
02:25
created

ClassRenderer::renderMethodBody()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
c 2
b 0
f 0
nc 1
nop 1
dl 0
loc 9
ccs 6
cts 6
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Proxy;
6
7
use Yiisoft\Proxy\Config\ClassConfig;
8
use Yiisoft\Proxy\Config\MethodConfig;
9
use Yiisoft\Proxy\Config\ParameterConfig;
10
use Yiisoft\Proxy\Config\TypeConfig;
11
12
final class ClassRenderer
13
{
14
    private string $classSignatureTemplate = '{{modifiers}} {{classType}} {{name}}{{extends}}{{parent}}{{implements}}';
15
16
    private string $proxyMethodSignatureTemplate = '{{modifiers}} function {{name}}({{params}}){{returnType}}';
17
18
    private string $proxyMethodBodyTemplate = '{{return}}$this->call({{methodName}}, [{{params}}]);';
19
20 6
    public function render(ClassConfig $classConfig): string
21
    {
22 6
        return trim($this->renderClassSignature($classConfig))
23
            . "\n"
24
            . '{'
25 6
            . $this->renderClassBody($classConfig)
26
            . '}';
27
    }
28
29 6
    private function renderClassSignature(ClassConfig $classConfig): string
30
    {
31 6
        $classType = $classConfig->isInterface
32 2
            ? 'interface'
33 4
            : 'class';
34 6
        $extends = $classConfig->parent
35 3
            ? ' extends '
36 3
            : '';
37
38 6
        return strtr($this->classSignatureTemplate, [
39 6
            '{{modifiers}}' => $this->renderModifiers($classConfig->modifiers),
40
            '{{classType}}' => $classType,
41 6
            '{{name}}' => $classConfig->shortName,
42
            '{{extends}}' => $extends,
43 6
            '{{parent}}' => $classConfig->parent,
44 6
            '{{implements}}' => $this->renderImplements($classConfig->interfaces),
45
        ]);
46
    }
47
48 6
    private function renderImplements(array $interfaces): string
49
    {
50 6
        if ($interfaces === []) {
51 3
            return '';
52
        }
53
54 3
        return ' implements ' . implode(', ', $interfaces);
55
    }
56
57 6
    private function renderModifiers(array $modifiers): string
58
    {
59 6
        return implode(' ', $modifiers);
60
    }
61
62 6
    private function renderClassBody(ClassConfig $classConfig): string
63
    {
64 6
        return $this->renderMethods($classConfig->methods);
65
    }
66
67
    /**
68
     * @param MethodConfig[] $methods
69
     *
70
     * @return string
71
     */
72 6
    private function renderMethods(array $methods): string
73
    {
74 6
        $methodsCode = '';
75 6
        foreach ($methods as $method) {
76 3
            $methodsCode .= "\n" . $this->renderMethod($method);
77
        }
78
79 6
        return $methodsCode;
80
    }
81
82 3
    private function renderMethod(MethodConfig $method): string
83
    {
84 3
        return $this->renderMethodSignature($method)
85 3
            . "\n" . $this->margin()
86
            . '{'
87 3
            . $this->renderMethodBody($method)
88 3
            . $this->margin()
89
            . '}'
90
            . "\n";
91
    }
92
93 3
    private function renderMethodSignature(MethodConfig $method): string
94
    {
95 3
        return strtr($this->proxyMethodSignatureTemplate, [
96 3
            '{{modifiers}}' => $this->margin() . $this->renderModifiers($method->modifiers),
97 3
            '{{name}}' => $method->name,
98 3
            '{{params}}' => $this->renderMethodParameters($method->parameters),
99 3
            '{{returnType}}' => $this->renderReturnType($method),
100
        ]);
101
    }
102
103 3
    private function renderMethodParameters(array $parameters): string
104
    {
105 3
        $params = '';
106 3
        foreach ($parameters as $parameter) {
107 2
            $params .= $this->renderMethodParameter($parameter) . ', ';
108
        }
109
110 3
        return rtrim($params, ', ');
111
    }
112
113 2
    private function renderMethodParameter(ParameterConfig $parameter): string
114
    {
115 2
        $type = $parameter->hasType
116 2
            ? $this->renderType($parameter->type)
0 ignored issues
show
Bug introduced by
It seems like $parameter->type can also be of type null; however, parameter $type of Yiisoft\Proxy\ClassRenderer::renderType() does only seem to accept Yiisoft\Proxy\Config\TypeConfig, maybe add an additional type check? ( Ignorable by Annotation )

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

116
            ? $this->renderType(/** @scrutinizer ignore-type */ $parameter->type)
Loading history...
117 1
            : '';
118 2
        $output = $type
119
            . ' $'
120 2
            . $parameter->name
121 2
            . $this->renderParameterDefaultValue($parameter);
122
123 2
        return ltrim($output);
124
    }
125
126 2
    private function renderParameterDefaultValue(ParameterConfig $parameter): string
127
    {
128 2
        if (!$parameter->isDefaultValueAvailable) {
129 2
            return '';
130
        }
131
132 1
        $value = $parameter->isDefaultValueConstant
133 1
            ? $parameter->defaultValueConstantName
134 1
            : self::varExport($parameter->defaultValue);
135
136 1
        return ' = ' . $value;
137
    }
138
139 3
    private function renderMethodBody(MethodConfig $method): string
140
    {
141 3
        $output = strtr($this->proxyMethodBodyTemplate, [
142 3
            '{{return}}' => $this->margin(2) . $this->renderReturn($method),
143 3
            '{{methodName}}' => "'" . $method->name . "'",
144 3
            '{{params}}' => $this->renderMethodCallParameters($method->parameters),
145
        ]);
146
147 3
        return "\n" . $output . "\n";
148
    }
149
150 3
    private function renderReturn(MethodConfig $method): string
151
    {
152 3
        if ($method->returnType?->name === 'void') {
153 1
            return '';
154
        }
155
156 3
        return 'return ';
157
    }
158
159 3
    private function renderReturnType(MethodConfig $method): string
160
    {
161 3
        if (!$method->hasReturnType) {
162 1
            return '';
163
        }
164
165 3
        return ': ' . $this->renderType($method->returnType);
0 ignored issues
show
Bug introduced by
It seems like $method->returnType can also be of type null; however, parameter $type of Yiisoft\Proxy\ClassRenderer::renderType() does only seem to accept Yiisoft\Proxy\Config\TypeConfig, maybe add an additional type check? ( Ignorable by Annotation )

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

165
        return ': ' . $this->renderType(/** @scrutinizer ignore-type */ $method->returnType);
Loading history...
166
    }
167
168 3
    private function renderType(TypeConfig $type): string
169
    {
170 3
        if ($type->name === 'mixed' || !$type->allowsNull) {
171 3
            return $type->name;
172
        }
173
174 1
        return '?' . $type->name;
175
    }
176
177 3
    private function renderMethodCallParameters(array $parameters): string
178
    {
179 3
        $keys = array_keys($parameters);
180 3
        if ($keys === []) {
181 2
            return '';
182
        }
183
184 2
        return '$' . implode(', $', $keys);
185
    }
186
187 1
    private static function varExport(mixed $var): string
188
    {
189 1
        $output = '';
190 1
        switch (gettype($var)) {
191 1
            case 'boolean':
192 1
                $output = $var
193 1
                    ? 'true'
194 1
                    : 'false';
195 1
                break;
196 1
            case 'integer':
197 1
            case 'double':
198 1
                $output = (string)$var;
199 1
                break;
200 1
            case 'string':
201 1
                $output = "'" . addslashes($var) . "'";
202 1
                break;
203 1
            case 'NULL':
204 1
                $output = 'null';
205 1
                break;
206 1
            case 'array':
207 1
                if (empty($var)) {
208 1
                    $output .= '[]';
209
                } else {
210 1
                    $keys = array_keys($var);
211 1
                    $output .= '[';
212 1
                    foreach ($keys as $index => $key) {
213 1
                        $output .= self::varExport($key);
214 1
                        $output .= ' => ';
215 1
                        $output .= self::varExport($var[$key]);
216
217 1
                        if ($index !== array_key_last($keys)) {
218 1
                            $output .= ', ';
219
                        }
220
                    }
221 1
                    $output .= ']';
222
                }
223 1
                break;
224
        }
225
226 1
        return $output;
227
    }
228
229 3
    private function margin(int $count = 1): string
230
    {
231 3
        return str_repeat('    ', $count);
232
    }
233
}
234