GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#166)
by Ross
47:38
created

DbalFieldGenerator   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 429
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 199
dl 0
loc 429
ccs 0
cts 284
cp 0
rs 10
c 0
b 0
f 0
wmc 30

13 Methods

Rating   Name   Duplication   Size   Complexity  
A create() 0 24 1
A setStringLengthValidation() 0 21 2
A setGetterToIsForBools() 0 9 2
A traitPostCopy() 0 6 1
A breakUpdateCallOntoMultipleLines() 0 14 1
A __construct() 0 14 1
A getPropertyMetaMethodForDatetime() 0 40 3
A postCopy() 0 16 1
A interfacePostCopy() 0 6 1
A generateTrait() 0 29 2
B replaceDefaultValueInInterface() 0 39 10
A generateInterface() 0 19 2
A getPropertyMetaMethod() 0 40 3
1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Field;
4
5
use Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder;
6
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\CodeHelper;
7
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\FileCreationTransaction;
8
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\FindAndReplaceHelper;
9
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\PathHelper;
10
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\TypeHelper;
11
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\UsesPHPMetaDataInterface;
12
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\ValidatedEntityInterface;
13
use EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException;
14
use EdmondsCommerce\DoctrineStaticMeta\MappingHelper;
15
use EdmondsCommerce\DoctrineStaticMeta\Schema\Database;
16
use gossi\codegen\model\PhpMethod;
17
use gossi\codegen\model\PhpParameter;
18
use gossi\codegen\model\PhpTrait;
19
use gossi\docblock\Docblock;
20
use gossi\docblock\tags\UnknownTag;
21
use Symfony\Component\Filesystem\Filesystem;
22
use Symfony\Component\Validator\Constraints\Length;
23
24
/**
25
 * Class DbalFieldGenerator
26
 *
27
 * @package  EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Field
28
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
29
 * @SuppressWarnings(PHPMD.TooManyFields)
30
 * @internal - this is only accessed via CodeGeneration\Generator\Field\FieldGenerator
31
 */
32
class DbalFieldGenerator
33
{
34
    /**
35
     * @var string
36
     */
37
    protected $traitPath;
38
    /**
39
     * @var string
40
     */
41
    protected $interfacePath;
42
    /**
43
     * @var null|string
44
     */
45
    protected $phpType;
46
    /**
47
     * @var null
48
     */
49
    protected $defaultValue;
50
    /**
51
     * @var bool
52
     */
53
    protected $isUnique;
54
    /**
55
     * @var bool
56
     */
57
    protected $isNullable;
58
    /**
59
     * @var string
60
     */
61
    protected $dbalType;
62
    /**
63
     * @var Filesystem
64
     */
65
    protected $fileSystem;
66
    /**
67
     * @var CodeHelper
68
     */
69
    protected $codeHelper;
70
    /**
71
     * @var FileCreationTransaction
72
     */
73
    protected $fileCreationTransaction;
74
    /**
75
     * @var FindAndReplaceHelper
76
     */
77
    protected $findAndReplaceHelper;
78
    /**
79
     * @var string
80
     */
81
    protected $className;
82
    /**
83
     * @var TypeHelper
84
     */
85
    protected $typeHelper;
86
    /**
87
     * @var string
88
     */
89
    protected $traitNamespace;
90
    /**
91
     * @var string
92
     */
93
    protected $interfaceNamespace;
94
    /**
95
     * @var PathHelper
96
     */
97
    protected $pathHelper;
98
99
    public function __construct(
100
        Filesystem $fileSystem,
101
        CodeHelper $codeHelper,
102
        FileCreationTransaction $fileCreationTransaction,
103
        FindAndReplaceHelper $findAndReplaceHelper,
104
        TypeHelper $typeHelper,
105
        PathHelper $pathHelper
106
    ) {
107
        $this->fileSystem              = $fileSystem;
108
        $this->codeHelper              = $codeHelper;
109
        $this->fileCreationTransaction = $fileCreationTransaction;
110
        $this->findAndReplaceHelper    = $findAndReplaceHelper;
111
        $this->typeHelper              = $typeHelper;
112
        $this->pathHelper              = $pathHelper;
113
    }
114
115
    /**
116
     * @param string      $className
117
     * @param string      $traitPath
118
     * @param string      $interfacePath
119
     * @param string      $dbalType
120
     * @param null        $defaultValue
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $defaultValue is correct as it would always require null to be passed?
Loading history...
121
     * @param bool        $isUnique
122
     * @param null|string $phpType
123
     *
124
     * @param string      $traitNamespace
125
     * @param string      $interfaceNamespace
126
     *
127
     * @return string
128
     * @throws DoctrineStaticMetaException
129
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
130
     */
131
    public function create(
132
        string $className,
133
        string $traitPath,
134
        string $interfacePath,
135
        string $dbalType,
136
        $defaultValue = null,
137
        bool $isUnique = false,
138
        ?string $phpType = null,
139
        string $traitNamespace,
140
        string $interfaceNamespace
141
    ): string {
142
        $this->traitPath          = $traitPath;
143
        $this->interfacePath      = $interfacePath;
144
        $this->phpType            = $phpType;
145
        $this->defaultValue       = $defaultValue;
146
        $this->isUnique           = $isUnique;
147
        $this->isNullable         = (null === $defaultValue);
148
        $this->dbalType           = $dbalType;
149
        $this->className          = $className;
150
        $this->traitNamespace     = $traitNamespace;
151
        $this->interfaceNamespace = $interfaceNamespace;
152
        $this->generateInterface();
153
154
        return $this->generateTrait();
155
    }
156
157
    /**
158
     * @throws DoctrineStaticMetaException
159
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
160
     */
161
    protected function generateInterface(): void
162
    {
163
        try {
164
            $this->fileSystem->copy(
165
                $this->pathHelper->resolvePath(FieldGenerator::FIELD_INTERFACE_TEMPLATE_PATH),
166
                $this->interfacePath
167
            );
168
            $this->interfacePostCopy($this->interfacePath);
169
            $this->codeHelper->replaceTypeHintsInFile(
170
                $this->interfacePath,
171
                $this->phpType,
0 ignored issues
show
Bug introduced by
It seems like $this->phpType can also be of type null; however, parameter $type of EdmondsCommerce\Doctrine...eplaceTypeHintsInFile() does only seem to accept string, 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

171
                /** @scrutinizer ignore-type */ $this->phpType,
Loading history...
172
                $this->dbalType,
173
                $this->isNullable
174
            );
175
        } catch (\Exception $e) {
176
            throw new DoctrineStaticMetaException(
177
                'Error in ' . __METHOD__ . ': ' . $e->getMessage(),
178
                $e->getCode(),
179
                $e
180
            );
181
        }
182
    }
183
184
    /**
185
     * @param string $filePath
186
     *
187
     * @throws DoctrineStaticMetaException
188
     */
189
    protected function interfacePostCopy(
190
        string $filePath
191
    ): void {
192
        $this->findAndReplaceHelper->replaceFieldInterfaceNamespace($this->interfaceNamespace, $filePath);
193
        $this->replaceDefaultValueInInterface($filePath);
194
        $this->postCopy($filePath);
195
    }
196
197
    /**
198
     * @param string $filePath
199
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
200
     */
201
    protected function replaceDefaultValueInInterface(
202
        string $filePath
203
    ): void {
204
        $defaultType = $this->typeHelper->getType($this->defaultValue);
205
        switch (true) {
206
            case $defaultType === 'null':
207
                $replace = 'null';
208
                break;
209
            case $this->phpType === 'string':
210
                $replace = "'$this->defaultValue'";
211
                break;
212
            case $this->phpType === 'bool':
213
                $replace = true === $this->defaultValue ? 'true' : 'false';
214
                break;
215
            case $this->phpType === 'float':
216
                $replace = (string)$this->defaultValue;
217
                if (false === \ts\stringContains($replace, '.')) {
218
                    $replace .= '.0';
219
                }
220
                break;
221
            case $this->phpType === 'int':
222
                $replace = (string)$this->defaultValue;
223
                break;
224
            case $this->phpType === trim(MappingHelper::PHP_TYPE_DATETIME, '\\'):
225
                if ($this->defaultValue !== null) {
226
                    throw new \InvalidArgumentException(
227
                        'Invalid default value ' . $this->defaultValue
228
                        . 'Currently we only support null as a default for DateTime'
229
                    );
230
                }
231
                $replace = 'null';
232
                break;
233
            default:
234
                throw new \RuntimeException(
235
                    'failed to calculate replace based on defaultType ' . $defaultType
236
                    . ' and phpType ' . $this->phpType . ' in ' . __METHOD__
237
                );
238
        }
239
        $this->findAndReplaceHelper->findReplace("'defaultValue'", $replace, $filePath);
240
    }
241
242
    /**
243
     * @param string $filePath
244
     *
245
     * @throws \RuntimeException
246
     * @throws DoctrineStaticMetaException
247
     */
248
    protected function postCopy(
249
        string $filePath
250
    ): void {
251
        $this->fileCreationTransaction::setPathCreated($filePath);
252
        $this->findAndReplaceHelper->replaceName(
253
            $this->codeHelper->classy($this->className),
254
            $filePath,
255
            FieldGenerator::FIND_ENTITY_FIELD_NAME
256
        );
257
        $this->findAndReplaceHelper->findReplace(
258
            $this->codeHelper->consty(FieldGenerator::FIND_ENTITY_FIELD_NAME),
259
            $this->codeHelper->consty($this->className),
260
            $filePath
261
        );
262
        $this->codeHelper->tidyNamespacesInFile($filePath);
263
        $this->setGetterToIsForBools($filePath);
264
    }
265
266
    protected function setGetterToIsForBools(
267
        string $filePath
268
    ): void {
269
        if ($this->phpType !== 'bool') {
270
            return;
271
        }
272
        $replaceName = $this->codeHelper->getGetterMethodNameForBoolean($this->codeHelper->classy($this->className));
273
        $findName    = 'get' . $this->codeHelper->classy($this->className);
274
        $this->findAndReplaceHelper->findReplace($findName, $replaceName, $filePath);
275
    }
276
277
    /**
278
     * @return string
279
     * @throws DoctrineStaticMetaException
280
     * @SuppressWarnings(PHPMD.StaticAccess)
281
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
282
     */
283
    protected function generateTrait(): string
284
    {
285
        try {
286
            $this->fileSystem->copy(
287
                $this->pathHelper->resolvePath(FieldGenerator::FIELD_TRAIT_TEMPLATE_PATH),
288
                $this->traitPath
289
            );
290
            $this->fileCreationTransaction::setPathCreated($this->traitPath);
291
            $this->traitPostCopy($this->traitPath);
292
            $trait = PhpTrait::fromFile($this->traitPath);
293
            $trait->setMethod($this->getPropertyMetaMethod());
294
            $trait->addUseStatement('\\' . MappingHelper::class);
295
            $trait->addUseStatement('\\' . ClassMetadataBuilder::class);
296
            $this->setStringLengthValidation($trait);
297
            $this->codeHelper->generate($trait, $this->traitPath);
298
            $this->codeHelper->replaceTypeHintsInFile(
299
                $this->traitPath,
300
                $this->phpType,
0 ignored issues
show
Bug introduced by
It seems like $this->phpType can also be of type null; however, parameter $type of EdmondsCommerce\Doctrine...eplaceTypeHintsInFile() does only seem to accept string, 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

300
                /** @scrutinizer ignore-type */ $this->phpType,
Loading history...
301
                $this->dbalType,
302
                $this->isNullable
303
            );
304
            $this->breakUpdateCallOntoMultipleLines();
305
306
            return $trait->getQualifiedName();
307
        } catch (\Exception $e) {
308
            throw new DoctrineStaticMetaException(
309
                'Error in ' . __METHOD__ . ': ' . $e->getMessage(),
310
                $e->getCode(),
311
                $e
312
            );
313
        }
314
    }
315
316
    /**
317
     * @param string $filePath
318
     *
319
     * @throws \RuntimeException
320
     * @throws DoctrineStaticMetaException
321
     */
322
    protected function traitPostCopy(
323
        string $filePath
324
    ): void {
325
        $this->findAndReplaceHelper->replaceFieldTraitNamespace($this->traitNamespace, $filePath);
326
        $this->findAndReplaceHelper->replaceFieldInterfaceNamespace($this->interfaceNamespace, $filePath);
327
        $this->postCopy($filePath);
328
    }
329
330
    /**
331
     * @return PhpMethod
332
     * @SuppressWarnings(PHPMD.StaticAccess)
333
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
334
     */
335
    protected function getPropertyMetaMethod(): PhpMethod
336
    {
337
        $classy = $this->codeHelper->classy($this->className);
338
        $consty = $this->codeHelper->consty($this->className);
339
        $name   = UsesPHPMetaDataInterface::METHOD_PREFIX_GET_PROPERTY_DOCTRINE_META . $classy;
340
        $method = PhpMethod::create($name);
341
        $method->setStatic(true);
342
        $method->setVisibility('public');
343
        $method->setParameters(
344
            [PhpParameter::create('builder')->setType('ClassMetadataBuilder')]
345
        );
346
        $mappingHelperMethodName = 'setSimple' . ucfirst(strtolower($this->dbalType)) . 'Fields';
347
348
        $methodBody = "
349
        MappingHelper::$mappingHelperMethodName(
350
            [{$classy}FieldInterface::PROP_{$consty}],
351
            \$builder,
352
            {$classy}FieldInterface::DEFAULT_{$consty}
353
        );                        
354
";
355
        if (\in_array($this->dbalType, MappingHelper::UNIQUEABLE_TYPES, true)) {
356
            $isUniqueString = $this->isUnique ? 'true' : 'false';
357
            $methodBody     = "
358
        MappingHelper::$mappingHelperMethodName(
359
            [{$classy}FieldInterface::PROP_{$consty}],
360
            \$builder,
361
            {$classy}FieldInterface::DEFAULT_{$consty},
362
            $isUniqueString
363
        );                        
364
";
365
        }
366
        $method->setBody($methodBody);
367
        $method->setDocblock(
368
            Docblock::create()
369
                    ->appendTag(
370
                        UnknownTag::create('SuppressWarnings(PHPMD.StaticAccess)')
371
                    )
372
        );
373
374
        return $method;
375
    }
376
377
    private function setStringLengthValidation(PhpTrait $trait): void
378
    {
379
        if ($this->dbalType !== MappingHelper::TYPE_STRING) {
380
            return;
381
        }
382
383
384
        $classy = $this->codeHelper->classy($this->className);
385
        $consty = $this->codeHelper->consty($this->className);
386
        $name   = ValidatedEntityInterface::METHOD_PREFIX_PROPERTY_VALIDATOR_META . $classy;
387
        $method = $trait->getMethod($name);
388
389
        $methodBody = <<<PHP
390
        \$metadata->addPropertyConstraint(
391
            {$classy}FieldInterface::PROP_{$consty},
392
            new Length(['min' => 0, 'max' => Database::MAX_VARCHAR_LENGTH])
393
        );
394
PHP;
395
        $method->setBody($methodBody);
396
        $trait->addUseStatement(Length::class);
397
        $trait->addUseStatement(Database::class);
398
    }
399
400
    private function breakUpdateCallOntoMultipleLines(): void
401
    {
402
        $contents = \ts\file_get_contents($this->traitPath);
403
        $indent   = '            ';
404
        $updated  = \preg_replace(
405
            [
406
                '%updatePropertyValue\((.+?),(.+?)\)%',
407
            ],
408
            [
409
                "updatePropertyValue(\n$indent\$1,\n$indent\$2\n        )",
410
            ],
411
            $contents
412
        );
413
        \file_put_contents($this->traitPath, $updated);
414
    }
415
416
    /**
417
     * @return PhpMethod
418
     * @SuppressWarnings(PHPMD.StaticAccess)
419
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
420
     */
421
    protected function getPropertyMetaMethodForDatetime(): PhpMethod
422
    {
423
        $classy = $this->codeHelper->classy($this->className);
424
        $consty = $this->codeHelper->consty($this->className);
425
        $name   = UsesPHPMetaDataInterface::METHOD_PREFIX_GET_PROPERTY_DOCTRINE_META . $classy;
426
        $method = PhpMethod::create($name);
427
        $method->setStatic(true);
428
        $method->setVisibility('public');
429
        $method->setParameters(
430
            [PhpParameter::create('builder')->setType('ClassMetadataBuilder')]
431
        );
432
        $mappingHelperMethodName = 'setSimple' . ucfirst(strtolower($this->dbalType)) . 'Fields';
433
434
        $methodBody = "
435
        MappingHelper::$mappingHelperMethodName(
436
            [{$classy}FieldInterface::PROP_{$consty}],
437
            \$builder,
438
            {$classy}FieldInterface::DEFAULT_{$consty}
439
        );                        
440
";
441
        if (\in_array($this->dbalType, MappingHelper::UNIQUEABLE_TYPES, true)) {
442
            $isUniqueString = $this->isUnique ? 'true' : 'false';
443
            $methodBody     = "
444
        MappingHelper::$mappingHelperMethodName(
445
            [{$classy}FieldInterface::PROP_{$consty}],
446
            \$builder,
447
            {$classy}FieldInterface::DEFAULT_{$consty},
448
            $isUniqueString
449
        );                        
450
";
451
        }
452
        $method->setBody($methodBody);
453
        $method->setDocblock(
454
            Docblock::create()
455
                    ->appendTag(
456
                        UnknownTag::create('SuppressWarnings(PHPMD.StaticAccess)')
457
                    )
458
        );
459
460
        return $method;
461
    }
462
}
463