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.
Passed
Pull Request — master (#192)
by joseph
40:19
created

Builder::extendInterfaceWithInterface()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 8
dl 0
loc 11
rs 10
c 0
b 0
f 0
ccs 0
cts 9
cp 0
nc 2
nop 2
cc 2
crap 6
1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta\Builder;
4
5
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Action\CreateDtosForAllEntitiesAction;
6
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\CodeHelper;
7
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Embeddable\ArchetypeEmbeddableGenerator;
8
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Embeddable\EntityEmbeddableSetter;
9
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\EntityGenerator;
10
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Field\EntityFieldSetter;
11
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Field\FieldGenerator;
12
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\RelationsGenerator;
13
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\NamespaceHelper;
14
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\PostProcessor\CopyPhpstormMeta;
15
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\PostProcessor\EntityFormatter;
16
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\UnusedRelationsRemover;
17
use EdmondsCommerce\DoctrineStaticMeta\Config;
18
use gossi\codegen\model\PhpClass;
19
use gossi\codegen\model\PhpConstant;
20
use gossi\codegen\model\PhpInterface;
21
use gossi\codegen\model\PhpTrait;
22
use ts\Reflection\ReflectionClass;
23
24
/**
25
 * Class Builder
26
 *
27
 * @package EdmondsCommerce\DoctrineStaticMeta\Builder
28
 * @SuppressWarnings(PHPMD)
29
 */
30
class Builder
31
{
32
33
    /**
34
     * @var EntityGenerator
35
     */
36
    protected $entityGenerator;
37
    /**
38
     * @var FieldGenerator
39
     */
40
    protected $fieldGenerator;
41
    /**
42
     * @var EntityFieldSetter
43
     */
44
    protected $fieldSetter;
45
    /**
46
     * @var RelationsGenerator
47
     */
48
    protected $relationsGenerator;
49
    /**
50
     * @var ArchetypeEmbeddableGenerator
51
     */
52
    protected $archetypeEmbeddableGenerator;
53
    /**
54
     * @var EntityEmbeddableSetter
55
     */
56
    protected $embeddableSetter;
57
    /**
58
     * @var CodeHelper
59
     */
60
    protected $codeHelper;
61
    /**
62
     * @var UnusedRelationsRemover
63
     */
64
    protected $unusedRelationsRemover;
65
    /**
66
     * @var CreateDtosForAllEntitiesAction
67
     */
68
    private $dataTransferObjectsForAllEntitiesAction;
69
    /**
70
     * @var EntityFormatter
71
     */
72
    private $entityFormatter;
73
    /**
74
     * @var CopyPhpstormMeta
75
     */
76
    private $copyPhpstormMeta;
77
    /**
78
     * @var NamespaceHelper
79
     */
80
    private $namespaceHelper;
81
82 1
    public function __construct(
83
        EntityGenerator $entityGenerator,
84
        FieldGenerator $fieldGenerator,
85
        EntityFieldSetter $fieldSetter,
86
        RelationsGenerator $relationsGenerator,
87
        ArchetypeEmbeddableGenerator $archetypeEmbeddableGenerator,
88
        EntityEmbeddableSetter $embeddableSetter,
89
        CodeHelper $codeHelper,
90
        UnusedRelationsRemover $unusedRelationsRemover,
91
        CreateDtosForAllEntitiesAction $dataTransferObjectsForAllEntitiesAction,
92
        EntityFormatter $entityFormatter,
93
        Config $config,
94
        CopyPhpstormMeta $copyPhpstormMeta,
95
        NamespaceHelper $namespaceHelper
96
    ) {
97 1
        $this->entityGenerator                         = $entityGenerator;
98 1
        $this->fieldGenerator                          = $fieldGenerator;
99 1
        $this->fieldSetter                             = $fieldSetter;
100 1
        $this->relationsGenerator                      = $relationsGenerator;
101 1
        $this->archetypeEmbeddableGenerator            = $archetypeEmbeddableGenerator;
102 1
        $this->embeddableSetter                        = $embeddableSetter;
103 1
        $this->codeHelper                              = $codeHelper;
104 1
        $this->unusedRelationsRemover                  = $unusedRelationsRemover;
105 1
        $this->dataTransferObjectsForAllEntitiesAction = $dataTransferObjectsForAllEntitiesAction;
106 1
        $this->entityFormatter                         = $entityFormatter;
107 1
        $this->copyPhpstormMeta                        = $copyPhpstormMeta;
108 1
        $this->namespaceHelper                         = $namespaceHelper;
109
110 1
        $this->setPathToProjectRoot($config::getProjectRootDirectory());
111 1
    }
112
113 1
    public function setPathToProjectRoot(string $pathToProjectRoot): self
114
    {
115 1
        $this->entityGenerator->setPathToProjectRoot($pathToProjectRoot);
116 1
        $this->fieldGenerator->setPathToProjectRoot($pathToProjectRoot);
117 1
        $this->fieldSetter->setPathToProjectRoot($pathToProjectRoot);
118 1
        $this->relationsGenerator->setPathToProjectRoot($pathToProjectRoot);
119 1
        $this->archetypeEmbeddableGenerator->setPathToProjectRoot($pathToProjectRoot);
120 1
        $this->unusedRelationsRemover->setPathToProjectRoot($pathToProjectRoot);
121 1
        $this->dataTransferObjectsForAllEntitiesAction->setProjectRootDirectory($pathToProjectRoot);
122 1
        $this->embeddableSetter->setPathToProjectRoot($pathToProjectRoot);
123 1
        $this->entityFormatter->setPathToProjectRoot($pathToProjectRoot);
124 1
        $this->copyPhpstormMeta->setPathToProjectRoot($pathToProjectRoot);
125
126 1
        return $this;
127
    }
128
129
    /**
130
     * @return EntityGenerator
131
     */
132
    public function getEntityGenerator(): EntityGenerator
133
    {
134
        return $this->entityGenerator;
135
    }
136
137
    /**
138
     * @return FieldGenerator
139
     */
140
    public function getFieldGenerator(): FieldGenerator
141
    {
142
        return $this->fieldGenerator;
143
    }
144
145
    /**
146
     * @return EntityFieldSetter
147
     */
148
    public function getFieldSetter(): EntityFieldSetter
149
    {
150
        return $this->fieldSetter;
151
    }
152
153
    /**
154
     * @return RelationsGenerator
155
     */
156
    public function getRelationsGenerator(): RelationsGenerator
157
    {
158
        return $this->relationsGenerator;
159
    }
160
161
    /**
162
     * @return ArchetypeEmbeddableGenerator
163
     */
164
    public function getArchetypeEmbeddableGenerator(): ArchetypeEmbeddableGenerator
165
    {
166
        return $this->archetypeEmbeddableGenerator;
167
    }
168
169
    /**
170
     * @return EntityEmbeddableSetter
171
     */
172
    public function getEmbeddableSetter(): EntityEmbeddableSetter
173
    {
174
        return $this->embeddableSetter;
175
    }
176
177
    /**
178
     * Finalise build - run various steps to wrap up the build and tidy up the codebase
179
     *
180
     * @return Builder
181
     */
182
    public function finaliseBuild(): self
183
    {
184
        $this->dataTransferObjectsForAllEntitiesAction->run();
185
        $this->entityFormatter->run();
186
        $this->copyPhpstormMeta->run();
187
188
        return $this;
189
    }
190
191
    /**
192
     * This step will remove any relations code that is not being used
193
     *
194
     * Generally it needs to be run in a separate PHP process to ensure PHP loads the final versions of code
195
     */
196
    public function removeUnusedRelations(): void
197
    {
198
        $this->unusedRelationsRemover->run();
199
    }
200
201
    /**
202
     * @param array $entityFqns
203
     *
204
     * @return Builder
205
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
206
     */
207
    public function generateEntities(array $entityFqns): self
208
    {
209
        $this->setProjectRootNamespace(
210
            $this->namespaceHelper->getProjectNamespaceRootFromEntityFqn(
211
                current($entityFqns)
212
            )
213
        );
214
        foreach ($entityFqns as $entityFqn) {
215
            $this->entityGenerator->generateEntity($entityFqn);
216
        }
217
218
        return $this;
219
    }
220
221
    public function setProjectRootNamespace(string $projectRootNamespace): self
222
    {
223
        $this->entityGenerator->setProjectRootNamespace($projectRootNamespace);
224
        $this->fieldGenerator->setProjectRootNamespace($projectRootNamespace);
225
        $this->fieldSetter->setProjectRootNamespace($projectRootNamespace);
226
        $this->relationsGenerator->setProjectRootNamespace($projectRootNamespace);
227
        $this->archetypeEmbeddableGenerator->setProjectRootNamespace($projectRootNamespace);
228
        $this->dataTransferObjectsForAllEntitiesAction->setProjectRootNamespace($projectRootNamespace);
229
        $this->embeddableSetter->setProjectRootNamespace($projectRootNamespace);
230
        $this->unusedRelationsRemover->setProjectRootNamespace($projectRootNamespace);
231
232
        return $this;
233
    }
234
235
    /**
236
     * @param array $entityRelationEntity
237
     *
238
     * @return Builder
239
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
240
     */
241
    public function setEntityRelations(array $entityRelationEntity): self
242
    {
243
        foreach ($entityRelationEntity as list($owningEntityFqn, $hasType, $ownedEntityFqn)) {
244
            $this->relationsGenerator->setEntityHasRelationToEntity($owningEntityFqn, $hasType, $ownedEntityFqn);
245
        }
246
247
        return $this;
248
    }
249
250
    /**
251
     * @param array $fields
252
     *
253
     * @return array $traitFqns
254
     */
255
    public function generateFields(array $fields): array
256
    {
257
        $traitFqns = [];
258
        foreach ($fields as list($fieldFqn, $fieldType)) {
259
            try {
260
                $traitFqns[] = $this->fieldGenerator->generateField($fieldFqn, $fieldType);
261
            } catch (\Exception $e) {
262
                throw new \RuntimeException(
263
                    'Failed building field with $fieldFqn: ' . $fieldFqn . ' and $fieldType ' . $fieldType,
264
                    $e->getCode(),
265
                    $e
266
                );
267
            }
268
        }
269
270
        return $traitFqns;
271
    }
272
273
    public function generateKeyedFields(array $fields): array
274
    {
275
        $traitFqns = [];
276
277
        $defaults = [
278
            FieldGenerator::FIELD_PHP_TYPE_KEY      => null,
279
            FieldGenerator::FIELD_DEFAULT_VAULE_KEY => null,
280
            FieldGenerator::FIELD_IS_UNIQUE_KEY     => false,
281
        ];
282
283
        foreach ($fields as $field) {
284
            /* Can not use list here as it breaks PHPMD */
285
            $combinedDefaults = $field + $defaults;
286
            $fieldFqn         = $combinedDefaults[FieldGenerator::FIELD_FQN_KEY];
287
            $fieldType        = $combinedDefaults[FieldGenerator::FIELD_TYPE_KEY];
288
            $phpType          = $combinedDefaults[FieldGenerator::FIELD_PHP_TYPE_KEY];
289
            $defaultValue     = $combinedDefaults[FieldGenerator::FIELD_DEFAULT_VAULE_KEY];
290
            $isUnique         = $combinedDefaults[FieldGenerator::FIELD_IS_UNIQUE_KEY];
291
            try {
292
                $traitFqns[] =
293
                    $this->fieldGenerator->generateField($fieldFqn, $fieldType, $phpType, $defaultValue, $isUnique);
294
            } catch (\Exception $e) {
295
                throw new \RuntimeException(
296
                    'Failed building field with $fieldFqn: ' . $fieldFqn . ' and $fieldType ' . $fieldType,
297
                    $e->getCode(),
298
                    $e
299
                );
300
            }
301
        }
302
303
        return $traitFqns;
304
    }
305
306
    /**
307
     * @param string $entityFqn
308
     * @param array  $fieldFqns
309
     *
310
     * @return Builder
311
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
312
     */
313
    public function setFieldsToEntity(string $entityFqn, array $fieldFqns): self
314
    {
315
        foreach ($fieldFqns as $fieldFqn) {
316
            $this->fieldSetter->setEntityHasField($entityFqn, $fieldFqn);
317
        }
318
319
        return $this;
320
    }
321
322
    /**
323
     * @param array $embeddables
324
     *
325
     * @return array $traitFqns
326
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
327
     * @throws \ReflectionException
328
     */
329
    public function generateEmbeddables(array $embeddables): array
330
    {
331
        $traitFqns = [];
332
        foreach ($embeddables as $embeddable) {
333
            list($archetypeEmbeddableObjectFqn, $newEmbeddableObjectClassName) = array_values($embeddable);
334
            $traitFqns[] = $this->archetypeEmbeddableGenerator->createFromArchetype(
335
                $archetypeEmbeddableObjectFqn,
336
                $newEmbeddableObjectClassName
337
            );
338
        }
339
340
        return $traitFqns;
341
    }
342
343
    /**
344
     * @param string $entityFqn
345
     * @param array  $embeddableTraitFqns
346
     *
347
     * @return Builder
348
     */
349
    public function setEmbeddablesToEntity(string $entityFqn, array $embeddableTraitFqns): self
350
    {
351
        foreach ($embeddableTraitFqns as $embeddableTraitFqn) {
352
            $this->embeddableSetter->setEntityHasEmbeddable($entityFqn, $embeddableTraitFqn);
353
        }
354
355
        return $this;
356
    }
357
358 1
    public function setEnumOptionsOnInterface(string $interfaceFqn, array $options): void
359
    {
360 1
        $pathToInterface = (new ReflectionClass($interfaceFqn))->getFileName();
361 1
        $basename        = basename($pathToInterface);
362 1
        $classy          = substr($basename, 0, strpos($basename, 'FieldInterface'));
363 1
        $consty          = $this->codeHelper->consty($classy);
364 1
        $interface       = PhpInterface::fromFile($pathToInterface);
365 1
        $constants       = $interface->getConstants();
366
        $constants->map(function (PhpConstant $constant) use ($interface, $consty) {
367 1
            if (0 === strpos($constant->getName(), $consty . '_OPTION')) {
368 1
                $interface->removeConstant($constant);
369
            }
370 1
            if (0 === strpos($constant->getName(), 'DEFAULT')) {
371 1
                $interface->removeConstant($constant);
372
            }
373 1
        });
374 1
        $optionConsts = [];
375 1
        foreach ($options as $option) {
376 1
            $name           = \str_replace(
377 1
                '__',
378 1
                '_',
379 1
                $consty . '_OPTION_' . $this->codeHelper->consty(
380 1
                    \str_replace(' ', '_', $option)
381
                )
382
            );
383 1
            $optionConsts[] = 'self::' . $name;
384 1
            $constant       = new PhpConstant($name, $option);
385 1
            $interface->setConstant($constant);
386
        }
387 1
        $interface->setConstant(
388 1
            new PhpConstant(
389 1
                $consty . '_OPTIONS',
390 1
                '[' . implode(",\n", $optionConsts) . ']',
391 1
                true
392
            )
393
        );
394 1
        $interface->setConstant(
395 1
            new PhpConstant(
396 1
                'DEFAULT_' . $consty,
397 1
                current($optionConsts),
398 1
                true
399
            )
400
        );
401 1
        $this->codeHelper->generate($interface, $pathToInterface);
402 1
    }
403
404
    public function injectTraitInToClass(string $traitFqn, string $classFqn): void
405
    {
406
        $classFilePath = $this->getFileName($classFqn);
407
        $class         = PhpClass::fromFile($classFilePath);
408
        $trait         = PhpTrait::fromFile($this->getFileName($traitFqn));
409
        $traits        = $class->getTraits();
410
        $exists        = array_search($traitFqn, $traits, true);
411
        if ($exists !== false) {
412
            return;
413
        }
414
        $class->addTrait($trait);
415
        $this->codeHelper->generate($class, $classFilePath);
416
    }
417
418
    public function extendInterfaceWithInterface(string $interfaceToExtendFqn, string $interfaceToAddFqn): void
419
    {
420
        $toExtendFilePath = $this->getFileName($interfaceToExtendFqn);
421
        $toExtend         = PhpInterface::fromFile($toExtendFilePath);
422
        $toAdd            = PhpInterface::fromFile($this->getFileName($interfaceToAddFqn));
423
        $exists           = $toExtend->getInterfaces()->contains($interfaceToAddFqn);
424
        if ($exists !== false) {
425
            return;
426
        }
427
        $toExtend->addInterface($toAdd);
428
        $this->codeHelper->generate($toExtend, $toExtendFilePath);
429
    }
430
431
    public function removeIdTraitFromClass(string $classFqn): void
432
    {
433
        $traitFqn = "DSM\\Fields\\Traits\\PrimaryKey\\IdFieldTrait";
434
        $this->removeTraitFromClass($classFqn, $traitFqn);
435
    }
436
437
    public function removeTraitFromClass(string $classFqn, string $traitFqn): void
438
    {
439
        $classPath = $this->getFileName($classFqn);
440
        $class     = PhpClass::fromFile($classPath);
441
        $traits    = $class->getTraits();
442
        if ($class->getUseStatements()->contains($traitFqn) === true) {
443
            $class->removeUseStatement($traitFqn);
444
        }
445
        $index = array_search($traitFqn, $traits, true);
446
        if ($index === false) {
447
            $shortNameParts = explode('\\', $traitFqn);
448
            $shortName      = (string)array_pop($shortNameParts);
449
            $index          = array_search($shortName, $traits, true);
450
        }
451
        if ($index === false) {
452
            return;
453
        }
454
        unset($traits[$index]);
455
        $reflectionClass = new ReflectionClass(PhpClass::class);
456
        $property        = $reflectionClass->getProperty('traits');
457
        $property->setAccessible(true);
458
        $property->setValue($class, $traits);
459
        $this->codeHelper->generate($class, $classPath);
460
    }
461
462
    private function getFileName(string $typeFqn): string
463
    {
464
        $reflectionClass = new ReflectionClass($typeFqn);
465
466
        return $reflectionClass->getFileName();
467
    }
468
}
469