Passed
Push — master ( 5ddf32...f0520f )
by Mike
02:13
created

DataProviderGenerator::convertUnderlines()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 1
dl 0
loc 8
ccs 6
cts 6
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
5
namespace Xervice\DataProvider\Business\Model\Generator;
6
7
8
use Nette\PhpGenerator\ClassType;
9
use Nette\PhpGenerator\Helpers;
10
use Nette\PhpGenerator\PhpNamespace;
11
use Xervice\DataProvider\Business\Model\DataProvider\DataProviderInterface;
12
use Xervice\DataProvider\Business\Model\Parser\DataProviderParserInterface;
13
14
class DataProviderGenerator implements DataProviderGeneratorInterface
15
{
16
    /**
17
     * @var \Xervice\DataProvider\Business\Model\Parser\DataProviderParserInterface
18
     */
19
    private $parser;
20
21
    /**
22
     * @var FileWriterInterface
23
     */
24
    private $fileWriter;
25
26
    /**
27
     * @var string
28
     */
29
    private $namespace;
30
31
    /**
32
     * @var string
33
     */
34
    private $dataProviderExtends;
35
36
    /**
37
     * DataProviderGenerator constructor.
38
     *
39
     * @param \Xervice\DataProvider\Business\Model\Parser\DataProviderParserInterface $parser
40
     * @param FileWriterInterface $fileWriter
41
     * @param string $namespace
42
     */
43 8
    public function __construct(
44
        DataProviderParserInterface $parser,
45
        FileWriterInterface $fileWriter,
46
        string $namespace,
47
        string $dataProviderExtends
48
    ) {
49 8
        $this->parser = $parser;
50 8
        $this->fileWriter = $fileWriter;
51 8
        $this->namespace = $namespace;
52 8
        $this->dataProviderExtends = $dataProviderExtends;
53 8
    }
54
55
    /**
56
     * @return array
57
     * @throws \Nette\InvalidArgumentException
58
     */
59 8
    public function generate(): array
60
    {
61 8
        $fileGenerated = [];
62
63 8
        foreach ($this->parser->getDataProvider() as $providerName => $providerData) {
64 8
            $namespace = new PhpNamespace($this->namespace);
65 8
            $dataProvider = $this->createDataProviderClass($providerName, $providerData, $namespace);
66 8
            $classContent = (string)$namespace;
67 8
            $classContent = str_replace('\?', '?', $classContent);
68 8
            $classContent = Helpers::tabsToSpaces($classContent, 4);
69 8
            $this->fileWriter->writeToFile($dataProvider->getName() . '.php', $classContent);
70 8
            $fileGenerated[] = $dataProvider->getName() . '.php';
71
        }
72
73 8
        return $fileGenerated;
74
    }
75
76
    /**
77
     * @param string $provider
78
     * @param \Nette\PhpGenerator\PhpNamespace $namespace
79
     *
80
     * @return ClassType
81
     * @throws \Nette\InvalidArgumentException
82
     */
83 8
    private function createNewDataProvider($provider, PhpNamespace $namespace): ClassType
84
    {
85 8
        $dataProvider = $namespace->addClass($provider . 'DataProvider');
86
        $dataProvider
87 8
            ->setFinal()
88 8
            ->setExtends($this->dataProviderExtends)
89 8
            ->setImplements(
90
                [
91 8
                    DataProviderInterface::class
92
                ]
93
            )
94 8
            ->setComment('Auto generated data provider')
95
        ;
96
97 8
        return $dataProvider;
98
    }
99
100
    /**
101
     * @param \Nette\PhpGenerator\ClassType $dataProvider
102
     * @param array $element
103
     * @param array $configs
104
     */
105 8
    private function addGetter(ClassType $dataProvider, array $element, array $configs): void
106
    {
107 8
        $dataProvider->addMethod('get' . $this->formatElementName($element['name'], $configs))
108 8
                     ->addComment('@return ' . $element['type'])
109 8
                     ->setVisibility('public')
110 8
                     ->setBody('return $this->' . $element['name'] . ';')
111 8
                     ->setReturnType($this->getTypeHint($element['type'], $element['allownull']))
112
        ;
113 8
    }
114
115
    /**
116
     * @param \Nette\PhpGenerator\ClassType $dataProvider
117
     * @param array $element
118
     * @param array $configs
119
     */
120 8
    private function addUnsetter(ClassType $dataProvider, array $element, array $configs): void
121
    {
122 8
        $dataProvider->addMethod('unset' . $this->formatElementName($element['name'], $configs))
123 8
                     ->addComment('@return ' . $dataProvider->getName())
124 8
                     ->setVisibility('public')
125 8
                     ->setBody('$this->' . $element['name'] . ' = null;' . PHP_EOL . PHP_EOL . 'return $this;')
126
        ;
127 8
    }
128
129
    /**
130
     * @param \Nette\PhpGenerator\ClassType $dataProvider
131
     * @param array $element
132
     * @param array $configs
133
     */
134 8
    private function addHas(ClassType $dataProvider, array $element, array $configs): void
135
    {
136 8
        $dataProvider->addMethod('has' . $this->formatElementName($element['name'], $configs))
137 8
                     ->addComment('@return bool')
138 8
                     ->setVisibility('public')
139 8
                     ->setBody(
140 8
                         'return ($this->' . $element['name'] . ' !== null && $this->' . $element['name'] . ' !== []);'
141
                     )
142
        ;
143 8
    }
144
145
    /**
146
     * @param \Nette\PhpGenerator\ClassType $dataProvider
147
     * @param array $element
148
     * @param array $configs
149
     */
150 8
    private function addSetter(ClassType $dataProvider, array $element, array $configs): void
151
    {
152 8
        $setter = $dataProvider->addMethod('set' . $this->formatElementName($element['name'], $configs))
153 8
                               ->addComment(
154 8
                                   '@param ' . $element['type'] . ' $'
155 8
                                   . $element['name']
156
                               )
157 8
                               ->addComment('@return ' . $dataProvider->getName())
158 8
                               ->setVisibility('public')
159 8
                               ->setBody(
160 8
                                   '$this->' . $element['name'] . ' = $' . $element['name'] . ';' . PHP_EOL . PHP_EOL
161 8
                                   . 'return $this;'
162
                               )
163
        ;
164
165 8
        $param = $setter->addParameter($element['name'])
166 8
                        ->setTypeHint($this->getTypeHint($element['type'], $element['allownull']))
167
        ;
168 8
        if ($element['default']) {
169 8
            $default = $this->getDefaultValue($element);
170 8
            $param->setDefaultValue($default);
171
        }
172 8
        elseif ($element['allownull']) {
173 8
            $param->setDefaultValue(null);
174
        }
175 8
    }
176
177
    /**
178
     * @param array $element
179
     * @param \Nette\PhpGenerator\ClassType $dataProvider
180
     * @param array $configs
181
     */
182 8
    private function addSingleSetter(array $element, ClassType $dataProvider, array $configs): void
183
    {
184 8
        if (isset($element['singleton']) && $element['singleton'] !== '') {
185 8
            $methodName = $configs['convertUnderlines']
186
                ? $this->convertUnderlines($element['singleton'])
187 8
                : $element['singleton'];
188
189
            $singleSetter = $dataProvider
190 8
                ->addMethod('add' . $methodName)
191 8
                ->addComment(
192 8
                    '@param ' . $element['singleton_type'] . ' $'
193 8
                    . $element['singleton']
194
                )
195 8
                ->addComment('@return ' . $dataProvider->getName())
196 8
                ->setVisibility('public')
197 8
                ->setBody(
198 8
                    sprintf(
199 8
                        '$this->%s[] = $%s; return $this;',
200 8
                        $element['name'],
201 8
                        $element['singleton']
202
                    )
203
                )
204
            ;
205
206 8
            $singleSetter->addParameter($element['singleton'])
207 8
                         ->setTypeHint($element['singleton_type'])
208
            ;
209
        }
210 8
    }
211
212
    /**
213
     * @param \Nette\PhpGenerator\ClassType $dataProvider
214
     * @param array $element
215
     */
216 8
    private function addProperty(ClassType $dataProvider, array $element): void
217
    {
218 8
        $property = $dataProvider->addProperty($element['name'])
219 8
                                 ->setVisibility('protected')
220 8
                                 ->addComment('@var ' . $element['type'])
221
        ;
222
223 8
        if ($element['default']) {
224 8
            $default = $this->getDefaultValue($element);
225 8
            $property->setValue($default);
226
        }
227 8
        elseif (strpos($element['type'], '[]') !== false) {
228 8
            $property->setValue([]);
229
        }
230 8
    }
231
232
    /**
233
     * @param string $type
234
     * @param bool $allowNull
235
     *
236
     * @return string
237
     */
238 8
    private function getTypeHint(string $type, bool $allowNull = null): string
239
    {
240 8
        if (strpos($type, '[]') !== false) {
241 8
            $type = 'array';
242
        }
243
244 8
        if ($allowNull === true) {
245 8
            $type = '?' . $type;
246
        }
247
248 8
        return $type;
249
    }
250
251
    /**
252
     * @param \Nette\PhpGenerator\ClassType $dataProvider
253
     * @param array $elements
254
     */
255 8
    private function addElementsGetter(ClassType $dataProvider, array $elements): void
256
    {
257 8
        $dataProvider->addMethod('getElements')
258 8
                     ->setReturnType('array')
259 8
                     ->setVisibility('protected')
260 8
                     ->addComment('@return array')
261 8
                     ->setBody('return ' . var_export($elements, true) . ';')
262
        ;
263 8
    }
264
265
    /**
266
     * @param string $providerName
267
     * @param array $providerData
268
     * @param \Nette\PhpGenerator\PhpNamespace $namespace
269
     *
270
     * @return \Nette\PhpGenerator\ClassType
271
     */
272 8
    private function createDataProviderClass(
273
        string $providerName,
274
        array $providerData,
275
        PhpNamespace $namespace
276
    ): ClassType {
277 8
        $dataProvider = $this->createNewDataProvider($providerName, $namespace);
278 8
        $providerElements = $providerData['elements'];
279
280 8
        foreach ($providerElements as $element) {
281 8
            $this->addProperty($dataProvider, $element);
282 8
            $this->addGetter($dataProvider, $element, $providerData['configs']);
283 8
            $this->addSetter($dataProvider, $element, $providerData['configs']);
284 8
            $this->addUnsetter($dataProvider, $element, $providerData['configs']);
285 8
            $this->addHas($dataProvider, $element, $providerData['configs']);
286 8
            $this->addSingleSetter($element, $dataProvider, $providerData['configs']);
287
288
        }
289
290 8
        $this->addElementsGetter($dataProvider, $providerElements);
291 8
        return $dataProvider;
292
    }
293
294
    /**
295
     * @param array $element
296
     *
297
     * @return bool
298
     */
299 8
    private function getDefaultValue($element)
300
    {
301 8
        $default = $element['default'];
302
303 8
        switch ($element['type']) {
304 8
            case 'bool':
305 8
            case 'boolean':
306
                {
307 8
                    $default = $default === 'false' ? false : $default;
308 8
                    $default = $default === 'true' ? true : $default;
309
                }
310 8
                break;
311
312 8
            case 'array':
313
                {
314 8
                    $default = [];
315
                }
316 8
                break;
317
318 8
            case 'string':
319
                {
320 8
                    $default = $default === '\'\'' ? '' : $default;
321
                }
322 8
                break;
323
        }
324
325 8
        settype($default, $element['type']);
326 8
        return $default;
327
    }
328
329
    /**
330
     * @param string $elementName
331
     *
332
     * @return string
333
     */
334 8
    private function formatElementName(string $elementName, array $configs): string
335
    {
336 8
        $elementName = ucfirst($elementName);
337
338 8
        if ($configs['convertUnderlines']) {
339 8
            $elementName = $this->convertUnderlines($elementName);
340
        }
341
342 8
        return $elementName;
343
    }
344
345
    /**
346
     * @param string $methodName
347
     *
348
     * @return string
349
     */
350 8
    private function convertUnderlines(string $methodName): string
351
    {
352 8
        return preg_replace_callback(
353 8
            '@\_([a-z]{1,1})@',
354
            function ($matches) {
355 8
                return strtoupper($matches[1] ?? '');
356 8
            },
357 8
            $methodName
358
        );
359
    }
360
}
361