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
Push — master ( 75bdf9...8faa57 )
by joseph
83:56 queued 81:04
created

FieldGenerator::assertFileDoesNotExist()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2.1481

Importance

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