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 ( a48bd8...83d092 )
by joseph
16:18 queued 18s
created

FakerDataFiller::guessMissingColumnFormatters()   B

Complexity

Conditions 9
Paths 8

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 0
Metric Value
cc 9
eloc 17
nc 8
nop 0
dl 0
loc 26
ccs 0
cts 24
cp 0
crap 90
rs 8.0555
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta\Entity\Testing\EntityGenerator;
4
5
use Doctrine\DBAL\Types\Type;
6
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\NamespaceHelper;
7
use EdmondsCommerce\DoctrineStaticMeta\DoctrineStaticMeta;
8
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\DataTransferObjectInterface;
9
use EdmondsCommerce\DoctrineStaticMeta\MappingHelper;
10
use Faker;
11
use Faker\ORM\Doctrine\ColumnTypeGuesser;
12
13
/**
14
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
15
 */
16
class FakerDataFiller
17
{
18
    /**
19
     * @var Faker\Generator
20
     */
21
    private static $generator;
22
23
    /**
24
     * These two are used to keep track of unique fields and ensure we dont accidently make apply none unique values
25
     *
26
     * @var array
27
     */
28
    private static $uniqueStrings = [];
29
    /**
30
     * @var int
31
     */
32
    private static $uniqueInt;
33
    /**
34
     * An array of fieldNames to class names that are to be instantiated as column formatters as required
35
     *
36
     * @var array|string[]
37
     */
38
    private $fakerDataProviderClasses;
39
40
    /**
41
     * A cache of instantiated column data providers
42
     *
43
     * @var array
44
     */
45
    private $fakerDataProviderObjects = [];
46
    /**
47
     * @var array
48
     */
49
    private $columnFormatters;
50
    /**
51
     * @var DoctrineStaticMeta
52
     */
53
    private $testedEntityDsm;
54
    /**
55
     * @var Faker\Guesser\Name
56
     */
57
    private $nameGuesser;
58
    /**
59
     * @var ColumnTypeGuesser
60
     */
61
    private $columnTypeGuesser;
62
    /**
63
     * @var NamespaceHelper
64
     */
65
    private $namespaceHelper;
66
67
    public function __construct(
68
        DoctrineStaticMeta $testedEntityDsm,
69
        NamespaceHelper $namespaceHelper,
70
        array $fakerDataProviderClasses,
71
        ?float $seed = null
72
    ) {
73
        $this->initFakerGenerator($seed);
74
        $this->testedEntityDsm          = $testedEntityDsm;
75
        $this->fakerDataProviderClasses = $fakerDataProviderClasses;
76
        $this->nameGuesser              = new \Faker\Guesser\Name(self::$generator);
77
        $this->columnTypeGuesser        = new ColumnTypeGuesser(self::$generator);
78
        $this->namespaceHelper          = $namespaceHelper;
79
        $this->checkFakerClassesRootNamespaceMatchesEntityFqn(
80
            $this->testedEntityDsm->getReflectionClass()->getName()
81
        );
82
        $this->generateColumnFormatters();
83
    }
84
85
    /**
86
     * @param float|null $seed
87
     * @SuppressWarnings(PHPMD.StaticAccess)
88
     */
89
    private function initFakerGenerator(?float $seed): void
90
    {
91
        if (null === self::$generator) {
92
            self::$generator = Faker\Factory::create();
93
        }
94
        if (null !== $seed) {
95
            self::$generator->seed($seed);
96
        }
97
    }
98
99
    private function checkFakerClassesRootNamespaceMatchesEntityFqn(string $fakedEntityFqn): void
100
    {
101
        if ([] === $this->fakerDataProviderClasses) {
102
            return;
103
        }
104
        $projectRootNamespace = null;
105
        foreach (array_keys($this->fakerDataProviderClasses) as $classField) {
106
            if (false === \ts\stringContains($classField, '-')) {
107
                continue;
108
            }
109
            list($entityFqn,) = explode('-', $classField);
110
            $rootNamespace = $this->namespaceHelper->getProjectNamespaceRootFromEntityFqn($entityFqn);
111
            if (null === $projectRootNamespace) {
112
                $projectRootNamespace = $rootNamespace;
113
                continue;
114
            }
115
            if ($rootNamespace !== $projectRootNamespace) {
116
                throw new \RuntimeException(
117
                    'Found unexpected root namespace ' .
118
                    $rootNamespace .
119
                    ', expecting ' .
120
                    $projectRootNamespace .
0 ignored issues
show
Bug introduced by
Are you sure $projectRootNamespace of type void can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

120
                    /** @scrutinizer ignore-type */ $projectRootNamespace .
Loading history...
121
                    ', do we have mixed fakerProviderClasses? ' .
122
                    print_r($this->fakerDataProviderClasses, true)
123
                );
124
            }
125
        }
126
        if (null === $projectRootNamespace) {
127
            return;
128
        }
129
        $fakedEntityRootNamespace = $this->namespaceHelper->getProjectNamespaceRootFromEntityFqn($fakedEntityFqn);
130
        if ($fakedEntityRootNamespace === $projectRootNamespace) {
131
            return;
132
        }
133
        throw new \RuntimeException('Faked entity FQN ' .
134
                                    $fakedEntityFqn .
135
                                    ' project root namespace does not match the faker classes root namespace ' .
136
                                    $projectRootNamespace);
137
    }
138
139
    /**
140
     * @throws \Doctrine\ORM\Mapping\MappingException
141
     */
142
    private function generateColumnFormatters(): void
143
    {
144
        $entityFqn  = $this->testedEntityDsm->getReflectionClass()->getName();
145
        $meta       = $this->testedEntityDsm->getMetaData();
146
        $fieldNames = $meta->getFieldNames();
147
        foreach ($fieldNames as $fieldName) {
148
            if (isset($this->columnFormatters[$fieldName])) {
149
                continue;
150
            }
151
            if (true === $this->addFakerDataProviderToColumnFormatters($fieldName, $entityFqn)) {
152
                continue;
153
            }
154
            $fieldMapping = $meta->getFieldMapping($fieldName);
155
            if (true === ($fieldMapping['unique'] ?? false)) {
156
                $this->addUniqueColumnFormatter($fieldMapping, $fieldName);
157
                continue;
158
            }
159
        }
160
        $this->guessMissingColumnFormatters();
161
    }
162
163
    /**
164
     * Add a faker data provider to the columnFormatters array (by reference) if there is one available
165
     *
166
     * Handles instantiating and caching of the data providers
167
     *
168
     * @param string $fieldName
169
     *
170
     * @param string $entityFqn
171
     *
172
     * @return bool
173
     */
174
    private function addFakerDataProviderToColumnFormatters(
175
        string $fieldName,
176
        string $entityFqn
177
    ): bool {
178
        foreach ([
179
                     $entityFqn . '-' . $fieldName,
180
                     $fieldName,
181
                 ] as $key) {
182
            if (!isset($this->fakerDataProviderClasses[$key])) {
183
                continue;
184
            }
185
            if (!isset($this->fakerDataProviderObjects[$key])) {
186
                $class                                = $this->fakerDataProviderClasses[$key];
187
                $this->fakerDataProviderObjects[$key] = new $class(self::$generator);
188
            }
189
            $this->columnFormatters[$fieldName] = $this->fakerDataProviderObjects[$key];
190
191
            return true;
192
        }
193
194
        return false;
195
    }
196
197
    private function addUniqueColumnFormatter(array &$fieldMapping, string $fieldName): void
198
    {
199
        switch ($fieldMapping['type']) {
200
            case MappingHelper::TYPE_UUID:
201
                return;
202
            case MappingHelper::TYPE_STRING:
203
                $this->columnFormatters[$fieldName] = $this->getUniqueString();
204
                break;
205
            case MappingHelper::TYPE_INTEGER:
206
            case Type::BIGINT:
207
                $this->columnFormatters[$fieldName] = $this->getUniqueInt();
208
                break;
209
            default:
210
                throw new \InvalidArgumentException('unique field has an unsupported type: '
211
                                                    . print_r($fieldMapping, true));
212
        }
213
    }
214
215
    private function getUniqueString(): string
216
    {
217
        $string = 'unique string: ' . $this->getUniqueInt() . md5((string)time());
218
        while (isset(self::$uniqueStrings[$string])) {
219
            $string                       = md5((string)time());
220
            self::$uniqueStrings[$string] = true;
221
        }
222
223
        return $string;
224
    }
225
226
    private function getUniqueInt(): int
227
    {
228
        return ++self::$uniqueInt;
229
    }
230
231
    private function guessMissingColumnFormatters(): void
232
    {
233
234
        $meta = $this->testedEntityDsm->getMetaData();
235
        foreach ($meta->getFieldNames() as $fieldName) {
236
            if (isset($this->columnFormatters[$fieldName])) {
237
                continue;
238
            }
239
            if ($meta->isIdentifier($fieldName) || !$meta->hasField($fieldName)) {
240
                continue;
241
            }
242
            if (false !== \ts\stringContains($fieldName, '.')) {
243
                continue;
244
            }
245
246
            $size = $meta->fieldMappings[$fieldName]['length'] ?? null;
247
            if (null !== $formatter = $this->guessByName($fieldName, $size)) {
248
                $this->columnFormatters[$fieldName] = $formatter;
249
                continue;
250
            }
251
            if (null !== $formatter = $this->columnTypeGuesser->guessFormat($fieldName, $meta)) {
252
                $this->columnFormatters[$fieldName] = $formatter;
253
                continue;
254
            }
255
            if ('json' === $meta->fieldMappings[$fieldName]['type']) {
256
                $this->columnFormatters[$fieldName] = $this->getJson();
257
            }
258
        }
259
    }
260
261
    private function guessByName(string $fieldName, ?int $size): ?\Closure
262
    {
263
        $formatter = $this->nameGuesser->guessFormat($fieldName, $size);
264
        if (null !== $formatter) {
0 ignored issues
show
introduced by
The condition null !== $formatter is always true.
Loading history...
265
            return $formatter;
266
        }
267
        if (false !== \ts\stringContains($fieldName, 'email')) {
268
            return function () {
269
                return self::$generator->email;
270
            };
271
        }
272
273
        return null;
274
    }
275
276
    private function getJson(): string
277
    {
278
        $toEncode                     = [];
279
        $toEncode['string']           = self::$generator->text;
280
        $toEncode['float']            = self::$generator->randomFloat();
281
        $toEncode['nested']['string'] = self::$generator->text;
282
        $toEncode['nested']['float']  = self::$generator->randomFloat();
283
284
        return json_encode($toEncode, JSON_PRETTY_PRINT);
285
    }
286
287
    public function fillDtoFieldsWithData(DataTransferObjectInterface $dto): void
288
    {
289
        foreach ($this->columnFormatters as $field => $formatter) {
290
            if (null === $formatter) {
291
                continue;
292
            }
293
            try {
294
                $value  = \is_callable($formatter) ? $formatter($dto) : $formatter;
295
                $setter = 'set' . $field;
296
                $dto->$setter($value);
297
            } catch (\InvalidArgumentException $ex) {
298
                throw new \InvalidArgumentException(
299
                    sprintf(
300
                        'Failed to generate a value for %s::%s: %s',
301
                        \get_class($dto),
302
                        $field,
303
                        $ex->getMessage()
304
                    )
305
                );
306
            }
307
        }
308
    }
309
}
310