Passed
Pull Request — master (#39)
by Alexander
04:47 queued 02:15
created

ClassRenderer::varExport()   B

Complexity

Conditions 11
Paths 9

Size

Total Lines 40
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 11

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 11
eloc 32
c 2
b 1
f 0
nc 9
nop 1
dl 0
loc 40
ccs 32
cts 32
cp 1
crap 11
rs 7.3166

1 Method

Rating   Name   Duplication   Size   Complexity  
A ClassRenderer::renderMethodParameter() 0 11 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
/**
13
 * Renders class contents based on a given config ({@see ClassConfig}).
14
 */
15
final class ClassRenderer
16
{
17
    /**
18
     * @var string A template for rendering class signature.
19
     *
20
     * @see renderClassSignature()
21
     */
22
    private string $classSignatureTemplate = '{{modifiers}} {{classType}} {{name}}{{extends}}{{parent}}{{implements}}';
23
24
    /**
25
     * @var string A template for rendering proxy method signature.
26
     *
27
     * @see renderMethodSignature()
28
     */
29
    private string $proxyMethodSignatureTemplate = '{{modifiers}} function {{name}}({{params}}){{returnType}}';
30
31
    /**
32
     * @var string A template for rendering proxy method body.
33
     *
34
     * @see renderMethodBody()
35
     */
36
    private string $proxyMethodBodyTemplate = '{{return}}$this->call({{methodName}}, [{{params}}]);';
37
38
    /**
39
     * Renders class contents to a string.
40
     *
41
     * @param ClassConfig $classConfig Class config.
42
     *
43
     * @return string Class contents as a string, opening PHP tag is not included.
44
     */
45 8
    public function render(ClassConfig $classConfig): string
46
    {
47 8
        return trim($this->renderClassSignature($classConfig))
48
            . "\n"
49
            . '{'
50 8
            . $this->renderClassBody($classConfig)
51
            . '}';
52
    }
53
54
    /**
55
     * Renders class / interface signature using {@see $classSignatureTemplate}.
56
     *
57
     * @param ClassConfig $classConfig Class config.
58
     *
59
     * @return string Class signature as a string.
60
     */
61 8
    private function renderClassSignature(ClassConfig $classConfig): string
62
    {
63 8
        $classType = $classConfig->isInterface
64 2
            ? 'interface'
65 6
            : 'class';
66 8
        $extends = $classConfig->parent
67 5
            ? ' extends '
68 3
            : '';
69
70 8
        return strtr($this->classSignatureTemplate, [
71 8
            '{{modifiers}}' => $this->renderModifiers($classConfig->modifiers),
72
            '{{classType}}' => $classType,
73 8
            '{{name}}' => $classConfig->shortName,
74
            '{{extends}}' => $extends,
75 8
            '{{parent}}' => $classConfig->parent,
76 8
            '{{implements}}' => $this->renderImplements($classConfig->interfaces),
77
        ]);
78
    }
79
80
    /**
81
     * Renders implements section. Used for interfaces Only
82
     *
83
     * @param string[] $interfaces A list of interfaces' names with namespaces.
84
     *
85
     * @return string Implements section as a string.
86
     *
87
     * @see ClassConfig::$interfaces
88
     */
89 8
    private function renderImplements(array $interfaces): string
90
    {
91 8
        if ($interfaces === []) {
92 3
            return '';
93
        }
94
95 5
        return ' implements ' . implode(', ', $interfaces);
96
    }
97
98
    /**
99
     * Renders modifiers section.
100
     *
101
     * @param string[] $modifiers A list of modifiers
102
     *
103
     * @return string Modifiers section as a string.
104
     *
105
     * @see ClassConfig::$modifiers
106
     */
107 8
    private function renderModifiers(array $modifiers): string
108
    {
109 8
        return implode(' ', $modifiers);
110
    }
111
112
    /**
113
     * Renders class body.
114
     *
115
     * @param ClassConfig $classConfig Class config.
116
     *
117
     * @return string Class body as a string.
118
     */
119 8
    private function renderClassBody(ClassConfig $classConfig): string
120
    {
121 8
        return $this->renderMethods($classConfig->methods);
122
    }
123
124
    /**
125
     * Renders all methods.
126
     *
127
     * @param MethodConfig[] $methods A list of method configs.
128
     *
129
     * @return string Methods' sequence as a string.
130
     *
131
     * @see ClassConfig::$methods
132
     */
133 8
    private function renderMethods(array $methods): string
134
    {
135 8
        $methodsCode = '';
136 8
        foreach ($methods as $method) {
137 5
            $methodsCode .= "\n" . $this->renderMethod($method);
138
        }
139
140 8
        return $methodsCode;
141
    }
142
143
    /**
144
     * Renders a single  method.
145
     *
146
     * @param MethodConfig $method Method config.
147
     *
148
     * @return string Method as a string.
149
     */
150 5
    private function renderMethod(MethodConfig $method): string
151
    {
152 5
        return $this->renderMethodSignature($method)
153 5
            . "\n" . $this->renderIndent()
154
            . '{'
155 5
            . $this->renderMethodBody($method)
156 5
            . $this->renderIndent()
157
            . '}'
158
            . "\n";
159
    }
160
161
    /**
162
     * Renders method signature using {@see $proxyMethodSignatureTemplate}.
163
     *
164
     * @param MethodConfig $method Method config.
165
     *
166
     * @return string Method signature as a string.
167
     */
168 5
    private function renderMethodSignature(MethodConfig $method): string
169
    {
170 5
        return strtr($this->proxyMethodSignatureTemplate, [
171 5
            '{{modifiers}}' => $this->renderIndent() . $this->renderModifiers($method->modifiers),
172 5
            '{{name}}' => $method->name,
173 5
            '{{params}}' => $this->renderMethodParameters($method->parameters),
174 5
            '{{returnType}}' => $this->renderReturnType($method),
175
        ]);
176
    }
177
178
    /**
179
     * Renders all parameters for a method.
180
     *
181
     * @param ParameterConfig[] $parameters A list of parameter configs.
182
     *
183
     * @return string Method parameters as a string.
184
     */
185 5
    private function renderMethodParameters(array $parameters): string
186
    {
187 5
        $params = '';
188 5
        foreach ($parameters as $parameter) {
189 3
            $params .= $this->renderMethodParameter($parameter) . ', ';
190
        }
191
192 5
        return rtrim($params, ', ');
193
    }
194
195
    /**
196
     * Renders a single parameter for a method.
197
     *
198
     * @param ParameterConfig $parameter Parameter config.
199
     *
200
     * @return string Method parameter as a string.
201
     */
202 3
    private function renderMethodParameter(ParameterConfig $parameter): string
203
    {
204 3
        $type = $parameter->hasType()
205 3
            ? $this->renderType($parameter->type)
206 1
            : '';
207 3
        $output = $type
208
            . ' $'
209 3
            . $parameter->name
210 3
            . $this->renderParameterDefaultValue($parameter);
211
212 3
        return ltrim($output);
213
    }
214
215
    /**
216
     * Renders default value for a parameter. Equal sign (surrounded with spaces) is included.
217
     *
218
     * @param ParameterConfig $parameter Parameter config.
219
     *
220
     * @return string Parameter's default value as a string.
221
     */
222 3
    private function renderParameterDefaultValue(ParameterConfig $parameter): string
223
    {
224 3
        if (!$parameter->isDefaultValueAvailable) {
225 3
            return '';
226
        }
227
228 1
        $value = $parameter->isDefaultValueConstant
229 1
            ? $parameter->defaultValueConstantName
230 1
            : var_export($parameter->defaultValue, true);
231
232 1
        return ' = ' . $value;
233
    }
234
235
    /**
236
     * Renders a method's body using {@see $proxyMethodBodyTemplate}.
237
     *
238
     * @param MethodConfig $method Method config.
239
     *
240
     * @return string Method body as a string.
241
     */
242 5
    private function renderMethodBody(MethodConfig $method): string
243
    {
244 5
        $output = strtr($this->proxyMethodBodyTemplate, [
245 5
            '{{return}}' => $this->renderIndent(2) . $this->renderReturn($method),
246 5
            '{{methodName}}' => "'" . $method->name . "'",
247 5
            '{{params}}' => $this->renderMethodCallParameters($method->parameters),
248
        ]);
249
250 5
        return "\n" . $output . "\n";
251
    }
252
253
    /**
254
     * Renders return statement for a method.
255
     *
256
     * @param MethodConfig $method Method config.
257
     *
258
     * @return string Return statement as a string.
259
     */
260 5
    private function renderReturn(MethodConfig $method): string
261
    {
262 5
        if ($method->returnType?->name === 'void') {
263 2
            return '';
264
        }
265
266 5
        return 'return ';
267
    }
268
269
    /**
270
     * Renders return type for a method.
271
     *
272
     * @param MethodConfig $method Method config.
273
     *
274
     * @return string Return type as a string.
275
     */
276 5
    private function renderReturnType(MethodConfig $method): string
277
    {
278 5
        if (!$method->hasReturnType()) {
279 1
            return '';
280
        }
281
282 5
        return ': ' . $this->renderType($method->returnType);
283
    }
284
285
    /**
286
     * Renders a type. Nullability is handled too.
287
     *
288
     * @param TypeConfig $type Type config.
289
     *
290
     * @return string Type as a string.
291
     */
292 5
    private function renderType(TypeConfig $type): string
293
    {
294 5
        if ($type->name === 'mixed' || !$type->allowsNull) {
295 5
            return $type->name;
296
        }
297
298 1
        return '?' . $type->name;
299
    }
300
301
    /**
302
     * Renders parameters passed to a proxy's method call.
303
     *
304
     * @param ParameterConfig[] $parameters A map where key is a {@see ParameterConfig::$name} and value is
305
     * {@see ParameterConfig} instance.
306
     * @psalm-param array<string, ParameterConfig> $parameters
307
     *
308
     * @return string Parameters as a string.
309
     */
310 5
    private function renderMethodCallParameters(array $parameters): string
311
    {
312 5
        $keys = array_keys($parameters);
313 5
        if ($keys === []) {
314 5
            return '';
315
        }
316
317 3
        return '$' . implode(', $', $keys);
318
    }
319
320
    /**
321
     * Renders indent. 4 spaces are used, with no tabs.
322
     *
323
     * @param int $count How many times indent should be repeated.
324
     *
325
     * @return string Indent as a string.
326
     */
327 5
    private function renderIndent(int $count = 1): string
328
    {
329 5
        return str_repeat('    ', $count);
330
    }
331
}
332