AbstractClassPrinter   A
last analyzed

Complexity

Total Complexity 38

Size/Duplication

Total Lines 257
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 116
c 0
b 0
f 0
dl 0
loc 257
ccs 0
cts 120
cp 0
rs 9.36
wmc 38

20 Methods

Rating   Name   Duplication   Size   Complexity  
B addParameter() 0 41 8
A getDefaultSignatures() 0 3 1
A getMethodPassReplacements() 0 3 1
A getClass() 0 3 1
A finishClass() 0 2 1
A getPrinter() 0 3 1
A addMethods() 0 4 2
A getParameterParts() 0 37 5
A getNamespace() 0 3 1
A getParameterTypeReplacements() 0 3 1
A __construct() 0 4 1
A getMethodGiveReplacements() 0 3 1
A getMethodBody() 0 5 2
A addMethod() 0 38 5
A getParameterNameReplacements() 0 3 1
A getClassFqn() 0 3 1
A print() 0 18 2
A printFile() 0 3 1
A mapSymbols() 0 3 1
A addImport() 0 3 1
1
<?php
2
3
namespace Leonidas\Console\Library\Printer\Model\Abstracts;
4
5
use Leonidas\Console\Library\Printer\Model\PsrPrinterFactory;
6
use Nette\PhpGenerator\ClassType;
7
use Nette\PhpGenerator\InterfaceType;
0 ignored issues
show
Bug introduced by
The type Nette\PhpGenerator\InterfaceType was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use Nette\PhpGenerator\Method;
9
use Nette\PhpGenerator\PhpFile;
10
use Nette\PhpGenerator\PhpNamespace;
11
use Nette\PhpGenerator\Printer;
12
use Nette\PhpGenerator\TraitType;
0 ignored issues
show
Bug introduced by
The type Nette\PhpGenerator\TraitType was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
14
abstract class AbstractClassPrinter
15
{
16
    public const CORE = 'core';
17
18
    protected const SIGNATURES = [];
19
20
    protected string $namespace;
21
22
    protected string $class;
23
24
    private array $imports = [];
25
26
    public function __construct(string $namespace, string $class)
27
    {
28
        $this->namespace = $namespace;
29
        $this->class = $class;
30
    }
31
32
    public function getNamespace(): string
33
    {
34
        return $this->namespace;
35
    }
36
37
    public function getClass(): string
38
    {
39
        return $this->class;
40
    }
41
42
    public function getClassFqn(): string
43
    {
44
        return $this->namespace . '\\' . $this->class;
45
    }
46
47
    public function printFile(): string
48
    {
49
        return $this->print($this->getDefaultSignatures());
50
    }
51
52
    protected function print(array $methods): string
53
    {
54
        $file = new PhpFile();
55
        $namespace = $file->addNamespace($this->namespace);
56
        $class = $this->setupClass($namespace);
57
58
        if (!$class->isInterface()) {
59
            $file->setStrictTypes(true);
60
        }
61
62
        $this->addMethods($class, $methods);
63
        $this->finishClass($class);
64
65
        array_map([$namespace, 'addUse'], $this->imports);
66
67
        $this->imports = [];
68
69
        return $this->getPrinter()->printFile($file);
70
    }
71
72
    protected function addImport(string $import): void
73
    {
74
        $this->imports[] = $import;
75
    }
76
77
    protected function getPrinter(): Printer
78
    {
79
        return PsrPrinterFactory::create();
80
    }
81
82
    protected function getDefaultSignatures(): array
83
    {
84
        return static::SIGNATURES;
85
    }
86
87
    /**
88
     * @param ClassType|InterfaceType|TraitType $class
89
     * @param array<string,array<string,string>> $signatures
90
     */
91
    protected function addMethods($class, array $signatures): void
92
    {
93
        foreach ($signatures as $method => $signature) {
94
            $this->addMethod($class, $method, $signature);
95
        }
96
    }
97
98
    /**
99
     * @param ClassType|InterfaceType|TraitType $class
100
     */
101
    protected function addMethod($class, string $name, array $signature): void
102
    {
103
        $take = $signature['take'] ?? '';
104
        $give = $signature['give'] ?? 'void';
105
        $call = $signature['call'] ?? $name;
106
        $pass = $signature['pass'] ?? '';
107
108
        $swap = $this->getMethodPassReplacements();
109
        $pass = str_replace($swap[0], $swap[1], $pass);
110
111
        $swap = $this->getMethodGiveReplacements();
112
        $return = str_replace(
113
            [...$swap[0], ...$strip = ['?', '&'], '$this'],
0 ignored issues
show
Bug introduced by
array($swap[0], $strip =...ray('?', '&'), '$this') of type array<integer,array|array<integer,string>|string> is incompatible with the type string|string[] expected by parameter $search of str_replace(). ( Ignorable by Annotation )

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

113
            /** @scrutinizer ignore-type */ [...$swap[0], ...$strip = ['?', '&'], '$this'],
Loading history...
114
            [...$swap[1], ...$this->mapSymbols($strip), $this->getClassFqn()],
0 ignored issues
show
Bug introduced by
array($swap[1], $this->m..., $this->getClassFqn()) of type array<integer,array|string> is incompatible with the type string|string[] expected by parameter $replace of str_replace(). ( Ignorable by Annotation )

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

114
            /** @scrutinizer ignore-type */ [...$swap[1], ...$this->mapSymbols($strip), $this->getClassFqn()],
Loading history...
115
            $give
116
        );
117
118
        $method = $class->addMethod($name)
119
            ->setVariadic(str_contains($take, '...'))
120
            ->setReturnNullable(str_starts_with($give, '?'))
121
            ->setReturnReference(str_starts_with($give, '&'))
122
            ->setReturnType($return)
123
            ->setPublic();
124
125
        if (!$class->isInterface()) {
126
            $method->setBody($this->getMethodBody($call, $pass, $give));
127
        }
128
129
        $params = ($take = $signature['take'] ?? false)
130
            ? explode(', ', $take)
131
            : [];
132
133
        foreach ($params as $param) {
134
            $this->addParameter($method, $param);
135
        }
136
137
        if ('$this' === $give) {
138
            $method->addComment('@return ' . '$this');
139
        }
140
    }
141
142
    protected function getMethodPassReplacements(): array
143
    {
144
        return [[], []];
145
    }
146
147
    protected function getMethodGiveReplacements(): array
148
    {
149
        return [[], []];
150
    }
151
152
    protected function getMethodBody(string $call, string $pass, string $give): string
153
    {
154
        $action = sprintf('$this->%s->%s(%s);', static::CORE, $call, $pass);
155
156
        return ('void' === $give) ? $action : 'return ' . $action;
157
    }
158
159
    protected function addParameter(Method $method, string $param): void
160
    {
161
        $parts = $this->getParameterParts($param);
162
163
        $swap = $this->getParameterNameReplacements();
164
        $name = str_replace(
165
            [...$swap[0], ...$strip = ['$', '?', '&', '...']],
0 ignored issues
show
Bug introduced by
array($swap[0], $strip =...('$', '?', '&', '...')) of type array<integer,array|array<integer,string>> is incompatible with the type string|string[] expected by parameter $search of str_replace(). ( Ignorable by Annotation )

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

165
            /** @scrutinizer ignore-type */ [...$swap[0], ...$strip = ['$', '?', '&', '...']],
Loading history...
166
            [...$swap[1], ...$this->mapSymbols($strip)],
0 ignored issues
show
Bug introduced by
array($swap[1], $this->mapSymbols($strip)) of type array<integer,array> is incompatible with the type string|string[] expected by parameter $replace of str_replace(). ( Ignorable by Annotation )

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

166
            /** @scrutinizer ignore-type */ [...$swap[1], ...$this->mapSymbols($strip)],
Loading history...
167
            $base = $parts['name']
168
        );
169
170
        $parameter = $method->addParameter($name)
171
            ->setReference(str_starts_with($base, '&'));
172
173
        if ($parts['has_type']) {
174
            $swap = $this->getParameterTypeReplacements();
175
            $type = str_replace(
176
                [...$swap[0], ...$strip = ['*']],
0 ignored issues
show
Bug introduced by
array($swap[0], $strip = array('*')) of type array<integer,array|array<integer,string>> is incompatible with the type string|string[] expected by parameter $search of str_replace(). ( Ignorable by Annotation )

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

176
                /** @scrutinizer ignore-type */ [...$swap[0], ...$strip = ['*']],
Loading history...
177
                [...$swap[1], ...$this->mapSymbols($strip)],
178
                $base = $parts['type']
179
            );
180
181
            $parameter->setType($type)->setNullable(str_starts_with($base, '?'));
182
        }
183
184
        if ($parts['has_default']) {
185
            $default = $parts['default'];
186
187
            if ('null' === $default) {
188
                $default = null;
189
            } elseif ('true' === $default) {
190
                $default = true;
191
            } elseif ('false' === $default) {
192
                $default = false;
193
            } elseif (is_numeric($default)) {
194
                $default = (int) $default;
195
            } elseif (is_string($default)) {
196
                $default = trim($default, '\'"');
197
            }
198
199
            $parameter->setDefaultValue($default);
200
        }
201
    }
202
203
    protected function getParameterParts(string $param): array
204
    {
205
        $param = preg_replace('/\s/', ' ', trim($param));
206
        $parts = array_pad(explode(' ', $param), 4, '');
207
208
        switch ($param) {
209
            case sprintf('%s %s = %s', $parts[0], $parts[1], $parts[3]):
210
                return [
211
                    'has_type' => true,
212
                    'has_default' => true,
213
                    'type' => $parts[0],
214
                    'name' => $parts[1],
215
                    'default' => $parts[3],
216
                ];
217
218
            case sprintf('%s %s', $parts[0], $parts[1]):
219
                return [
220
                    'has_type' => true,
221
                    'has_default' => false,
222
                    'type' => $parts[0],
223
                    'name' => $parts[1],
224
                ];
225
226
            case sprintf('%s = %s', $parts[0], $parts[1]):
227
                return [
228
                    'has_type' => false,
229
                    'has_default' => true,
230
                    'name' => $parts[0],
231
                    'default' => $parts[1],
232
                ];
233
234
            case sprintf('%s', $parts[0]):
235
            default:
236
                return [
237
                    'has_type' => false,
238
                    'has_default' => false,
239
                    'name' => $parts[0],
240
                ];
241
        }
242
    }
243
244
    protected function mapSymbols(array $symbols): array
245
    {
246
        return array_pad([], count($symbols), '');
247
    }
248
249
    protected function getParameterTypeReplacements(): array
250
    {
251
        return [[], []];
252
    }
253
254
    protected function getParameterNameReplacements(): array
255
    {
256
        return [[], []];
257
    }
258
259
    /**
260
     * @param ClassType|InterfaceType|TraitType $class
261
     */
262
    protected function finishClass($class): void
263
    {
264
        //
265
    }
266
267
    /**
268
     * @return ClassType|TraitType|InterfaceType
269
     */
270
    abstract protected function setupClass(PhpNamespace $namespace): object;
271
}
272