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 (#59)
by joseph
18:36
created

FieldGenerator::createDbalField()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.584
c 0
b 0
f 0
ccs 18
cts 18
cp 1
cc 1
nc 1
nop 0
crap 1
1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Field;
4
5
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\CodeHelper;
6
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\AbstractGenerator;
7
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\FileCreationTransaction;
8
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\FindAndReplaceHelper;
9
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\NamespaceHelper;
10
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\PathHelper;
11
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\TypeHelper;
12
use EdmondsCommerce\DoctrineStaticMeta\Config;
13
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Boolean\ApprovedFieldTrait;
0 ignored issues
show
Bug introduced by
The type EdmondsCommerce\Doctrine...lean\ApprovedFieldTrait 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...
14
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Boolean\DefaultFieldTrait;
0 ignored issues
show
Bug introduced by
The type EdmondsCommerce\Doctrine...olean\DefaultFieldTrait 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...
15
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Boolean\DefaultsDisabledFieldTrait;
16
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Boolean\DefaultsEnabledFieldTrait;
17
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Boolean\DefaultsNullFieldTrait;
18
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\DateTime\DateTimeSettableNoDefaultFieldTrait;
19
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\DateTime\DateTimeSettableOnceFieldTrait;
20
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\String\BusinessIdentifierCodeFieldTrait;
21
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\String\CountryCodeFieldTrait;
22
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\String\EmailAddressFieldTrait;
23
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\String\IpAddressFieldTrait;
24
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\String\IsbnFieldTrait;
25
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\String\LocaleIdentifierFieldTrait;
26
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\String\NullableStringFieldTrait;
27
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\String\SettableUuidFieldTrait;
28
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\String\UnicodeLanguageIdentifierFieldTrait;
29
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\String\UniqueStringFieldTrait;
30
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\String\UrlFieldTrait;
31
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\TimeStamp\ActionedDateFieldTrait;
0 ignored issues
show
Bug introduced by
The type EdmondsCommerce\Doctrine...\ActionedDateFieldTrait 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...
32
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\TimeStamp\ActivatedDateFieldTrait;
0 ignored issues
show
Bug introduced by
The type EdmondsCommerce\Doctrine...ActivatedDateFieldTrait 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...
33
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\TimeStamp\CompletedDateFieldTrait;
0 ignored issues
show
Bug introduced by
The type EdmondsCommerce\Doctrine...CompletedDateFieldTrait 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...
34
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\TimeStamp\CreationTimestampFieldTrait;
35
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\TimeStamp\DeactivatedDateFieldTrait;
0 ignored issues
show
Bug introduced by
The type EdmondsCommerce\Doctrine...activatedDateFieldTrait 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...
36
use EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException;
37
use EdmondsCommerce\DoctrineStaticMeta\MappingHelper;
38
use Symfony\Component\Filesystem\Filesystem;
39
40
/**
41
 * Class FieldGenerator
42
 *
43
 * @package EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator
44
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
45
 */
46
class FieldGenerator extends AbstractGenerator
47
{
48
    public const FIELD_TRAIT_SUFFIX = 'FieldTrait';
49
50
    /**
51
     * @var string
52
     */
53
    protected $fieldsPath;
54
55
    /**
56
     * @var string
57
     */
58
    protected $fieldsInterfacePath;
59
60
    /**
61
     * @var string
62
     */
63
    protected $phpType;
64
65
    /**
66
     * @var string
67
     */
68
    protected $fieldType;
69
70
    /**
71
     * Are we currently generating an archetype based field?
72
     *
73
     * @var bool
74
     */
75
    protected $isArchetype = false;
76
77
    /**
78
     * @var bool
79
     */
80
    protected $isNullable;
81
82
    /**
83
     * @var bool
84
     */
85
    protected $isUnique;
86
87
    /**
88
     * @var mixed
89
     */
90
    protected $defaultValue;
91
92
    /**
93
     * @var string
94
     */
95
    protected $traitNamespace;
96
97
    /**
98
     * @var string
99
     */
100
    protected $interfaceNamespace;
101
102
    public const STANDARD_FIELDS = [
103
        DefaultsDisabledFieldTrait::class,
104
        DefaultsEnabledFieldTrait::class,
105
        DefaultsNullFieldTrait::class,
106
        DateTimeSettableNoDefaultFieldTrait::class,
107
        DateTimeSettableOnceFieldTrait::class,
108
        BusinessIdentifierCodeFieldTrait::class,
109
        CountryCodeFieldTrait::class,
110
        EmailAddressFieldTrait::class,
111
        IpAddressFieldTrait::class,
112
        IsbnFieldTrait::class,
113
        LocaleIdentifierFieldTrait::class,
114
        NullableStringFieldTrait::class,
115
        SettableUuidFieldTrait::class,
116
        UnicodeLanguageIdentifierFieldTrait::class,
117
        UniqueStringFieldTrait::class,
118
        UrlFieldTrait::class,
119
        CreationTimestampFieldTrait::class,
120
    ];
121
122
    /**
123
     * @var TypeHelper
124
     */
125
    protected $typeHelper;
126
    /**
127
     * @var ArchetypeFieldGenerator
128
     */
129
    protected $archetypeFieldCopier;
130
    /**
131
     * @var string
132
     */
133
    protected $fieldFqn;
134
    /**
135
     * @var string
136
     */
137
    protected $className;
138
139
140 35
    public function __construct(
141
        Filesystem $filesystem,
142
        FileCreationTransaction $fileCreationTransaction,
143
        NamespaceHelper $namespaceHelper,
144
        Config $config,
145
        CodeHelper $codeHelper,
146
        PathHelper $pathHelper,
147
        FindAndReplaceHelper $findAndReplaceHelper,
148
        TypeHelper $typeHelper
149
    ) {
150 35
        parent::__construct(
151 35
            $filesystem,
152 35
            $fileCreationTransaction,
153 35
            $namespaceHelper,
154 35
            $config,
155 35
            $codeHelper,
156 35
            $pathHelper,
157 35
            $findAndReplaceHelper
158
        );
159 35
        $this->typeHelper = $typeHelper;
160 35
    }
161
162
163
    /**
164
     * Generate a new Field based on a property name and Doctrine Type or Archetype field FQN
165
     *
166
     * @see MappingHelper::ALL_DBAL_TYPES for the full list of Dbal Types
167
     *
168
     * @param string      $fieldFqn
169
     * @param string      $fieldType
170
     * @param null|string $phpType
171
     *
172
     * @param mixed       $defaultValue
173
     * @param bool        $isUnique
174
     *
175
     * @return string - The Fully Qualified Name of the generated Field Trait
176
     *
177
     * @throws DoctrineStaticMetaException
178
     * @throws \ReflectionException
179
     * @SuppressWarnings(PHPMD.StaticAccess)
180
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
181
     */
182 34
    public function generateField(
183
        string $fieldFqn,
184
        string $fieldType,
185
        ?string $phpType = null,
186
        $defaultValue = null,
187
        bool $isUnique = false
188
    ): string {
189 34
        $this->validateArguments($fieldFqn, $fieldType, $phpType);
190 30
        $this->setupClassProperties($fieldFqn, $fieldType, $phpType, $defaultValue, $isUnique);
191
192 30
        $this->pathHelper->ensurePathExists($this->fieldsPath);
193 30
        $this->pathHelper->ensurePathExists($this->fieldsInterfacePath);
194
195 30
        $this->assertFileDoesNotExist($this->getTraitPath(), 'Trait');
196 30
        $this->assertFileDoesNotExist($this->getInterfacePath(), 'Interface');
197
198 30
        if (true === $this->isArchetype) {
199 3
            return $this->createFieldFromArchetype();
200
        }
201
202 28
        return $this->createDbalField();
203
    }
204
205 30
    protected function getTraitPath(): string
206
    {
207 30
        return $this->fieldsPath.'/'.$this->codeHelper->classy($this->className).'FieldTrait.php';
208
    }
209
210
211 30
    protected function getInterfacePath(): string
212
    {
213 30
        return $this->fieldsInterfacePath.'/'.$this->codeHelper->classy($this->className).'FieldInterface.php';
214
    }
215
216
    /**
217
     * @return string
218
     * @throws DoctrineStaticMetaException
219
     */
220 28
    protected function createDbalField(): string
221
    {
222 28
        $creator = new DbalFieldGenerator(
223 28
            $this->fileSystem,
224 28
            $this->codeHelper,
225 28
            $this->fileCreationTransaction,
226 28
            $this->findAndReplaceHelper,
227 28
            $this->typeHelper,
228 28
            $this->pathHelper
229
        );
230
231 28
        return $creator->create(
232 28
            $this->className,
233 28
            $this->getTraitPath(),
234 28
            $this->getInterfacePath(),
235 28
            $this->fieldType,
236 28
            $this->defaultValue,
237 28
            $this->isUnique,
238 28
            $this->phpType,
239 28
            $this->traitNamespace,
240 28
            $this->interfaceNamespace
241
        );
242
    }
243
244
    /**
245
     * @return string
246
     * @throws \ReflectionException
247
     */
248 3
    protected function createFieldFromArchetype(): string
249
    {
250 3
        $copier = new ArchetypeFieldGenerator(
251 3
            $this->fileSystem,
252 3
            $this->namespaceHelper,
253 3
            $this->codeHelper,
254 3
            $this->findAndReplaceHelper
255
        );
256
257 3
        return $copier->createFromArchetype(
258 3
            $this->fieldFqn,
259 3
            $this->getTraitPath(),
260 3
            $this->getInterfacePath(),
261 3
            '\\'.$this->fieldType,
262 3
            $this->projectRootNamespace
263 3
        ).self::FIELD_TRAIT_SUFFIX;
264
    }
265
266 34
    protected function validateArguments(
267
        string $fieldFqn,
268
        string $fieldType,
269
        ?string $phpType
270
    ): void {
271
        //Check for a correct looking field FQN
272 34
        if (false === \strpos($fieldFqn, AbstractGenerator::ENTITY_FIELD_TRAIT_NAMESPACE)) {
273 1
            throw new \InvalidArgumentException(
274 1
                'Fully qualified name [ '.$fieldFqn.' ]'
275 1
                .' does not include [ '.AbstractGenerator::ENTITY_FIELD_TRAIT_NAMESPACE.' ].'."\n"
276 1
                .'Please ensure you pass in the full namespace qualified field name'
277
            );
278
        }
279
        //Check that the field type is either a Dbal Type or a Field Archetype FQN
280 33
        if (false === \in_array(\strtolower($fieldType), MappingHelper::ALL_DBAL_TYPES, true)
281 33
            && false === \in_array($fieldType, self::STANDARD_FIELDS, true)
282 33
            && false === $this->traitFqnLooksLikeField($fieldType)
283
        ) {
284
            throw new \InvalidArgumentException(
285
                'fieldType '.$fieldType.' is not a valid field type'
286
            );
287
        }
288
        //Check the phpType is valid
289 32
        if ((null !== $phpType)
290 32
            && (false === \in_array($phpType, MappingHelper::PHP_TYPES, true))
291
        ) {
292 2
            throw new \InvalidArgumentException(
293 2
                'phpType must be either null or one of MappingHelper::PHP_TYPES'
294
            );
295
        }
296 30
    }
297
298
    /**
299
     * Does the specified trait FQN look like a field trait?
300
     *
301
     * @param string $traitFqn
302
     *
303
     * @return bool
304
     * @throws \ReflectionException
305
     */
306 2
    protected function traitFqnLooksLikeField(string $traitFqn): bool
307
    {
308
        try {
309 2
            $reflection = new \ReflectionClass($traitFqn);
310 1
        } catch (\ReflectionException $e) {
311 1
            throw new \InvalidArgumentException(
312 1
                'invalid traitFqn '.$traitFqn.' does not seem to exist',
313 1
                $e->getCode(),
314 1
                $e
315
            );
316
        }
317 1
        if (true !== $reflection->isTrait()) {
318
            throw new \InvalidArgumentException('field type is not a trait FQN');
319
        }
320 1
        if ('FieldTrait' !== \substr($traitFqn, -\strlen('FieldTrait'))) {
321
            throw new \InvalidArgumentException('traitFqn does not end in FieldTrait');
322
        }
323
324 1
        return true;
325
    }
326
327
    /**
328
     * Defining the properties for the field to be generated
329
     *
330
     * @param string      $fieldFqn
331
     * @param string      $fieldType
332
     * @param null|string $phpType
333
     * @param mixed       $defaultValue
334
     * @param bool        $isUnique
335
     *
336
     * @throws DoctrineStaticMetaException
337
     * @SuppressWarnings(PHPMD.StaticAccess)
338
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
339
     */
340 30
    protected function setupClassProperties(
341
        string $fieldFqn,
342
        string $fieldType,
343
        ?string $phpType,
344
        $defaultValue,
345
        bool $isUnique
346
    ): void {
347 30
        $this->fieldType = \strtolower($fieldType);
348 30
        if (true !== \in_array($this->fieldType, MappingHelper::COMMON_TYPES, true)) {
349 3
            $this->isArchetype = true;
350 3
            $this->fieldType   = $fieldType;
351
        }
352 30
        $this->phpType      = $phpType ?? $this->getPhpTypeForDbalType();
353 30
        $this->defaultValue = $this->typeHelper->normaliseValueToType($defaultValue, $this->phpType);
354
355 30
        if (null !== $this->defaultValue) {
356 1
            $defaultValueType = $this->typeHelper->getType($this->defaultValue);
357 1
            if ($defaultValueType !== $this->phpType) {
358
                throw new \InvalidArgumentException(
359
                    'default value '.$this->defaultValue.' has the type: '.$defaultValueType
360
                    .' whereas the phpType for this field has been set as '.$this->phpType.', these do not match up'
361
                );
362
            }
363
        }
364 30
        $this->isNullable = (null === $defaultValue);
365 30
        $this->isUnique   = $isUnique;
366
367 30
        if (\substr($fieldFqn, -\strlen(self::FIELD_TRAIT_SUFFIX)) === self::FIELD_TRAIT_SUFFIX) {
368 4
            $fieldFqn = \substr($fieldFqn, 0, -\strlen(self::FIELD_TRAIT_SUFFIX));
369
        }
370 30
        $this->fieldFqn = $fieldFqn;
371
372 30
        list($className, $traitNamespace, $traitSubDirectories) = $this->parseFullyQualifiedName(
373 30
            $fieldFqn,
374 30
            $this->srcSubFolderName
375
        );
376 30
        $this->className = $className;
377 30
        list(, $interfaceNamespace, $interfaceSubDirectories) = $this->parseFullyQualifiedName(
378 30
            \str_replace('Traits', 'Interfaces', $fieldFqn),
379 30
            $this->srcSubFolderName
380
        );
381
382 30
        $this->fieldsPath = $this->pathHelper->resolvePath(
383 30
            $this->pathToProjectRoot.'/'.\implode('/', $traitSubDirectories)
384
        );
385
386 30
        $this->fieldsInterfacePath = $this->pathHelper->resolvePath(
387 30
            $this->pathToProjectRoot.'/'.\implode('/', $interfaceSubDirectories)
388
        );
389
390 30
        $this->traitNamespace     = $traitNamespace;
391 30
        $this->interfaceNamespace = $interfaceNamespace;
392 30
    }
393
394 30
    private function assertFileDoesNotExist(string $filePath, string $type): void
395
    {
396 30
        if (file_exists($filePath)) {
397 1
            throw new \RuntimeException("Field $type already exists at $filePath");
398
        }
399 30
    }
400
401
    /**
402
     * @return string
403
     * @throws DoctrineStaticMetaException
404
     *
405
     */
406 30
    protected function getPhpTypeForDbalType(): string
407
    {
408 30
        if (true === $this->isArchetype) {
409 3
            return '';
410
        }
411 28
        if (!\in_array($this->fieldType, MappingHelper::COMMON_TYPES, true)) {
412
            throw new DoctrineStaticMetaException(
413
                'Field type of '.$this->fieldType.' is not one of MappingHelper::COMMON_TYPES'
414
                ."\n\nYou can only use this fieldType type if you pass in the explicit phpType as well "
415
                ."\n\nAlternatively, suggest you set the type as string and then edit the generated code as you see fit"
416
            );
417
        }
418
419 28
        return MappingHelper::COMMON_TYPES_TO_PHP_TYPES[$this->fieldType];
420
    }
421
}
422