Failed Conditions
Push — develop ( 856053...a7d1bd )
by Guilherme
61:28
created

ClassMetadataFactory::doLoadMetadata()   D

Complexity

Conditions 15
Paths 345

Size

Total Lines 75
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 15.0083

Importance

Changes 0
Metric Value
dl 0
loc 75
ccs 29
cts 30
cp 0.9667
rs 4
c 0
b 0
f 0
cc 15
eloc 42
nc 345
nop 3
crap 15.0083

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM\Mapping;
6
7
use Doctrine\DBAL\Platforms;
8
use Doctrine\ORM\EntityManagerInterface;
9
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
10
use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs;
11
use Doctrine\ORM\Events;
12
use Doctrine\ORM\ORMException;
13
use Doctrine\ORM\Sequencing;
14
use Doctrine\ORM\Sequencing\Planning\ColumnValueGeneratorExecutor;
15
use Doctrine\ORM\Sequencing\Planning\CompositeValueGenerationPlan;
16
use Doctrine\ORM\Sequencing\Planning\NoopValueGenerationPlan;
17
use Doctrine\ORM\Sequencing\Planning\SingleValueGenerationPlan;
18
use ReflectionException;
19
20
/**
21
 * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
22
 * metadata mapping information of a class which describes how a class should be mapped
23
 * to a relational database.
24
 *
25
 * @since   2.0
26
 * @author  Benjamin Eberlei <[email protected]>
27
 * @author  Guilherme Blanco <[email protected]>
28
 * @author  Jonathan Wage <[email protected]>
29
 * @author  Roman Borschel <[email protected]>
30
 */
31
class ClassMetadataFactory extends AbstractClassMetadataFactory
32
{
33
    /**
34
     * @var EntityManagerInterface|null
35
     */
36
    private $em;
37
38
    /**
39
     * @var \Doctrine\DBAL\Platforms\AbstractPlatform
40
     */
41
    private $targetPlatform;
42
43
    /**
44
     * @var Driver\MappingDriver
45
     */
46
    private $driver;
47
48
    /**
49
     * @var \Doctrine\Common\EventManager
50
     */
51
    private $evm;
52
53
    /**
54
     * {@inheritdoc}
55
     */
56
    protected function loadMetadata(string $name, ClassMetadataBuildingContext $metadataBuildingContext) : array
57
    {
58
        $loaded = parent::loadMetadata($name, $metadataBuildingContext);
59
60
        array_map([$this, 'resolveDiscriminatorValue'], $loaded);
61
62
        return $loaded;
63
    }
64
65
    /**
66
     * @param EntityManagerInterface $em
67
     */
68
    public function setEntityManager(EntityManagerInterface $em)
69
    {
70
        $this->em = $em;
71
    }
72
73
    /**
74
     * {@inheritdoc}
75 397
     *
76
     * @throws ORMException
77 397
     */
78
    protected function initialize() : void
79 380
    {
80
        $this->driver = $this->em->getConfiguration()->getMetadataDriverImpl();
81 380
        $this->evm = $this->em->getEventManager();
82
        $this->initialized = true;
83
    }
84
85
    /**
86
     * {@inheritdoc}
87 2292
     */
88
    protected function onNotFoundMetadata(
89 2292
        string $className,
90 2292
        ClassMetadataBuildingContext $metadataBuildingContext
91
    ) : ?ClassMetadata
92
    {
93
        if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {
94
            return null;
95 465
        }
96
97 465
        $eventArgs = new OnClassMetadataNotFoundEventArgs($className, $metadataBuildingContext, $this->em);
0 ignored issues
show
Bug introduced by
It seems like $this->em can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
98 465
99 465
        $this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs);
100 465
101
        return $eventArgs->getFoundMetadata();
102
    }
103
104
    /**
105 2
     * {@inheritdoc}
106
     *
107 2
     * @throws MappingException
108
     * @throws ORMException
109
     */
110
    protected function doLoadMetadata(
111 2
        string $className,
112
        ?ClassMetadata $parent,
113 2
        ClassMetadataBuildingContext $metadataBuildingContext
114
    ) : ClassMetadata
115 2
    {
116
        $classMetadata = new ClassMetadata($className, $metadataBuildingContext);
117
118
        if ($parent) {
119
            $classMetadata->setParent($parent);
120
121 395
            $this->addInheritedProperties($classMetadata, $parent);
122
123
            $classMetadata->setInheritanceType($parent->inheritanceType);
124
            $classMetadata->setIdentifier($parent->identifier);
125 395
126 108
            if ($parent->discriminatorColumn) {
127 29
                $classMetadata->setDiscriminatorColumn($parent->discriminatorColumn);
128
                $classMetadata->setDiscriminatorMap($parent->discriminatorMap);
129
            }
130 108
131 108
            $classMetadata->setLifecycleCallbacks($parent->lifecycleCallbacks);
132 107
            $classMetadata->setChangeTrackingPolicy($parent->changeTrackingPolicy);
133
134 107
            if ($parent->isMappedSuperclass) {
135 107
                $classMetadata->setCustomRepositoryClassName($parent->getCustomRepositoryClassName());
136 107
            }
137
        }
138 107
139 4
        // Invoke driver
140
        try {
141
            $this->driver->loadMetadataForClass($classMetadata->getClassName(), $classMetadata, $metadataBuildingContext);
142 107
        } catch (ReflectionException $e) {
143 65
            throw MappingException::reflectionFailure($classMetadata->getClassName(), $e);
144 65
        }
145
146
        $this->completeIdentifierGeneratorMappings($classMetadata);
147 107
148 107
        if ($parent) {
149
            $this->addInheritedNamedQueries($classMetadata, $parent);
150 107
151 1
            if ($parent->getCache()) {
152
                $classMetadata->setCache(clone $parent->getCache());
153
            }
154 107
155 39
            if ( ! empty($parent->namedNativeQueries)) {
156
                $this->addInheritedNamedNativeQueries($classMetadata, $parent);
157
            }
158
159
            if ( ! empty($parent->sqlResultSetMappings)) {
160
                $this->addInheritedSqlResultSetMappings($classMetadata, $parent);
161 395
            }
162 2
163
            if ( ! empty($parent->entityListeners) && empty($classMetadata->entityListeners)) {
164
                $classMetadata->entityListeners = $parent->entityListeners;
165
            }
166
        }
167
168
        if (! $classMetadata->discriminatorMap && $classMetadata->inheritanceType !== InheritanceType::NONE && $classMetadata->isRootEntity()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $classMetadata->discriminatorMap of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
169 393
            $this->addDefaultDiscriminatorMap($classMetadata);
170 74
        }
171
172 389
        $this->completeRuntimeMetadata($classMetadata, $parent);
173
174
        if ($this->evm->hasListeners(Events::loadClassMetadata)) {
175
            $eventArgs = new LoadClassMetadataEventArgs($classMetadata, $this->em);
0 ignored issues
show
Bug introduced by
It seems like $this->em can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
176
177
            $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
178
        }
179
180
        $this->buildValueGenerationPlan($classMetadata);
181
        $this->validateRuntimeMetadata($classMetadata, $parent);
182
183
        return $classMetadata;
184
    }
185
186
    /**
187
     * @param ClassMetadata      $class
188
     * @param ClassMetadata|null $parent
189
     *
190
     * @return void
191
     */
192
    protected function completeRuntimeMetadata(ClassMetadata $class, ClassMetadata $parent = null) : void
193
    {
194
        if (! $parent || ! $parent->isMappedSuperclass) {
195
            return;
196
        }
197
198
        if ($class->isMappedSuperclass) {
199
            return;
200
        }
201
202
        $tableName = $class->getTableName();
203
204
        // Resolve column table names
205
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
206
            if ($property instanceof FieldMetadata) {
207
                $property->setTableName($property->getTableName() ?? $tableName);
208
209 391
                continue;
210 107
            }
211 29
212
            if (! ($property instanceof ToOneAssociationMetadata)) {
213
                continue;
214 107
            }
215
216 107
            // Resolve association join column table names
217 3
            foreach ($property->getJoinColumns() as $joinColumn) {
218
                /** @var JoinColumnMetadata $joinColumn */
219
                $joinColumn->setTableName($joinColumn->getTableName() ?? $tableName);
220 107
            }
221
        }
222
    }
223
224 107
    /**
225 1
     * Validate runtime metadata is correctly defined.
226
     *
227
     * @param ClassMetadata      $class
228 107
     * @param ClassMetadata|null $parent
229 8
     *
230
     * @return void
231
     *
232 107
     * @throws MappingException
233 8
     */
234
    protected function validateRuntimeMetadata(ClassMetadata $class, ClassMetadata $parent = null) : void
235
    {
236 107
        if (! $class->getReflectionClass()) {
237 11
            // only validate if there is a reflection class instance
238
            return;
239
        }
240
241 391
        $class->validateIdentifier();
242
        $class->validateAssociations();
243 391
        $class->validateLifecycleCallbacks($this->getReflectionService());
244 1
245
        // verify inheritance
246
        if ( ! $class->isMappedSuperclass && $class->inheritanceType !== InheritanceType::NONE) {
247 391
            if ( ! $parent) {
248
                if (! $class->discriminatorMap) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class->discriminatorMap of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
249 391
                    throw MappingException::missingDiscriminatorMap($class->getClassName());
250 6
                }
251
252 6
                if ( ! $class->discriminatorColumn) {
253
                    throw MappingException::missingDiscriminatorColumn($class->getClassName());
254
                }
255 390
            }
256 385
        } else if (($class->discriminatorMap || $class->discriminatorColumn) && $class->isMappedSuperclass && $class->isRootEntity()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class->discriminatorMap of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
257
            // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy
258
            throw MappingException::noInheritanceOnMappedSuperClass($class->getClassName());
259
        }
260
    }
261
262
    /**
263
     * {@inheritdoc}
264 391
     */
265
    protected function newClassMetadataBuildingContext() : ClassMetadataBuildingContext
266 391
    {
267 387
        return new ClassMetadataBuildingContext(
268
            $this,
269
            $this->getReflectionService(),
270 107
            $this->em->getConfiguration()->getNamingStrategy()
271 74
        );
272
    }
273
274 39
    /**
275 1
     * Populates the discriminator value of the given metadata (if not set) by iterating over discriminator
276
     * map classes and looking for a fitting one.
277
     *
278 39
     * @param ClassMetadata $metadata
279
     *
280
     * @return void
281 39
     *
282 39
     * @throws \InvalidArgumentException
283
     * @throws \ReflectionException
284
     * @throws MappingException
285
     */
286 39
    private function resolveDiscriminatorValue(ClassMetadata $metadata) : void
287 13
    {
288 10
        if ($metadata->discriminatorValue || ! $metadata->discriminatorMap || $metadata->isMappedSuperclass ||
0 ignored issues
show
Bug Best Practice introduced by
The expression $metadata->discriminatorMap of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
289 13
            ! $metadata->getReflectionClass() || $metadata->getReflectionClass()->isAbstract()) {
290
            return;
291
        }
292
293
        // minor optimization: avoid loading related metadata when not needed
294
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
295
            if ($discriminatorClass === $metadata->getClassName()) {
296
                $metadata->discriminatorValue = $discriminatorValue;
297
298
                return;
299
            }
300 39
        }
301
302
        // iterate over discriminator mappings and resolve actual referenced classes according to existing metadata
303
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
304
            if ($metadata->getClassName() === $this->getMetadataFor($discriminatorClass)->getClassName()) {
305
                $metadata->discriminatorValue = $discriminatorValue;
306
307
                return;
308
            }
309
        }
310
311
        throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->getClassName(), $metadata->getRootClassName());
312 390
    }
313
314 390
    /**
315
     * Adds a default discriminator map if no one is given
316 5
     *
317
     * If an entity is of any inheritance type and does not contain a
318
     * discriminator map, then the map is generated automatically. This process
319 385
     * is expensive computation wise.
320 381
     *
321 381
     * The automatically generated discriminator map contains the lowercase short name of
322
     * each class as key.
323
     *
324 381
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
325 67
     *
326 63
     * @throws MappingException
327
     */
328
    private function addDefaultDiscriminatorMap(ClassMetadata $class) : void
329
    {
330 63
        $allClasses = $this->driver->getAllClassNames();
331 67
        $fqcn       = $class->getClassName();
332
        $map        = [$this->getShortName($fqcn) => $fqcn];
333
        $duplicates = [];
334 349
335
        foreach ($allClasses as $subClassCandidate) {
336 1
            if (is_subclass_of($subClassCandidate, $fqcn)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if $fqcn can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
337
                $shortName = $this->getShortName($subClassCandidate);
338 380
339
                if (isset($map[$shortName])) {
340
                    $duplicates[] = $shortName;
341
                }
342
343 390
                $map[$shortName] = $class->fullyQualifiedClassName($subClassCandidate);
344
            }
345 390
        }
346
347
        if ($duplicates) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $duplicates of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
348
            throw MappingException::duplicateDiscriminatorEntry($class->getClassName(), $duplicates, $map);
349
        }
350
351
        $class->setDiscriminatorMap($map);
352
    }
353
354
    /**
355
     * Gets the lower-case short name of a class.
356
     *
357
     * @param string $className
358 380
     *
359
     * @return string
360 380
     */
361 380
    private function getShortName($className) : string
362 380
    {
363
        if (strpos($className, "\\") === false) {
364
            return strtolower($className);
365
        }
366 5
367 5
        $parts = explode("\\", $className);
368 3
369
        return strtolower(end($parts));
370 5
    }
371
372
    /**
373
     * Adds inherited fields to the subclass mapping.
374
     *
375 2
     * @param ClassMetadata $subClass
376 2
     * @param ClassMetadata $parentClass
377 1
     *
378
     * @return void
379 2
     *
380
     * @throws MappingException
381
     */
382
    private function addInheritedProperties(ClassMetadata $subClass, ClassMetadata $parentClass) : void
383 1
    {
384
        $isAbstract = $parentClass->isMappedSuperclass;
385
386
        foreach ($parentClass->getDeclaredPropertiesIterator() as $fieldName => $property) {
387
            if ($isAbstract && $property instanceof ToManyAssociationMetadata && ! $property->isOwningSide()) {
388
                throw MappingException::illegalToManyAssociationOnMappedSuperclass($parentClass->getClassName(), $fieldName);
389
            }
390
391
            $subClass->addInheritedProperty($property);
392
        }
393
    }
394
395
    /**
396
     * Adds inherited named queries to the subclass mapping.
397
     *
398
     * @since 2.2
399
     *
400 1
     * @param ClassMetadata $subClass
401
     * @param ClassMetadata $parentClass
402 1
     *
403 1
     * @return void
404 1
     *
405 1
     * @throws MappingException
406
     */
407 1
    private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass) : void
408 1
    {
409 1
        foreach ($parentClass->getNamedQueries() as $name => $query) {
410
            if ($subClass->hasNamedQuery($name)) {
411 1
                continue;
412
            }
413
414
            $subClass->addNamedQuery($name, $query);
415 1
        }
416
    }
417
418
    /**
419 1
     * Adds inherited named native queries to the subclass mapping.
420
     *
421
     * @since 2.3
422
     *
423 1
     * @param ClassMetadata $subClass
424 1
     * @param ClassMetadata $parentClass
425
     *
426
     * @return void
427
     *
428
     * @throws MappingException
429
     */
430
    private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass) : void
431
    {
432
        foreach ($parentClass->namedNativeQueries as $name => $query) {
433 1
            if (isset($subClass->namedNativeQueries[$name])) {
434
                continue;
435 1
            }
436
437
            $subClass->addNamedNativeQuery(
438
                $name,
439 1
                $query['query'],
440
                [
441 1
                    'resultSetMapping' => $query['resultSetMapping'],
442
                    'resultClass'      => $query['resultClass'],
443
                ]
444
            );
445
        }
446
    }
447
448
    /**
449
     * Adds inherited sql result set mappings to the subclass mapping.
450
     *
451
     * @since 2.3
452 108
     *
453
     * @param ClassMetadata $subClass
454 108
     * @param ClassMetadata $parentClass
455 92
     *
456
     * @return void
457
     *
458 108
     * @throws MappingException
459 93
     */
460
    private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass) : void
461 108
    {
462
        foreach ($parentClass->sqlResultSetMappings as $name => $mapping) {
463
            if (isset ($subClass->sqlResultSetMappings[$name])) {
464
                continue;
465
            }
466
467
            $entities = [];
468
469
            foreach ($mapping['entities'] as $entity) {
470
                $entities[] = [
471
                    'fields'              => $entity['fields'],
472
                    'discriminatorColumn' => $entity['discriminatorColumn'],
473 108
                    'entityClass'         => $entity['entityClass'],
474
                ];
475 108
            }
476
477 108
            $subClass->addSqlResultSetMapping(
478 40
                [
479 6
                    'name'     => $mapping['name'],
480 1
                    'columns'  => $mapping['columns'],
481
                    'entities' => $entities,
482
                ]
483 5
            );
484
        }
485
    }
486
487 39
    /**
488 34
     * Completes the ID generator mapping. If "auto" is specified we choose the generator
489 34
     * most appropriate for the targeted database platform.
490 34
     *
491
     * @param ClassMetadata $class
492
     *
493
     * @return void
494
     *
495 39
     * @throws ORMException
496 34
     */
497
    private function completeIdentifierGeneratorMappings(ClassMetadata $class) : void
498
    {
499 39
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
500
            if ( ! $property instanceof FieldMetadata /*&& ! $property instanceof AssocationMetadata*/) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
501 107
                continue;
502
            }
503
504
            $this->completeFieldIdentifierGeneratorMapping($property);
505
        }
506
    }
507
508
    private function completeFieldIdentifierGeneratorMapping(FieldMetadata $field)
509
    {
510
        if (!$field->hasValueGenerator()) {
511 107
            return;
512
        }
513
514
        $platform  = $this->getTargetPlatform();
515
        $class     = $field->getDeclaringClass();
0 ignored issues
show
Unused Code introduced by
$class is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
516
        $generator = $field->getValueGenerator();
517
518
        if ($generator->getType() === GeneratorType::AUTO) {
519
            $generator = new ValueGeneratorMetadata(
520
                $platform->prefersSequences()
521
                    ? GeneratorType::SEQUENCE
522
                    : ($platform->prefersIdentityColumns()
523
                        ? GeneratorType::IDENTITY
524 107
                        : GeneratorType::TABLE
525
                ),
526
                $field->getValueGenerator()->getDefinition()
527
            );
528
            $field->setValueGenerator($generator);
529
        }
530
531
        // Validate generator definition and set defaults where needed
532
        switch ($generator->getType()) {
533
            case GeneratorType::SEQUENCE:
534
                // If there is no sequence definition yet, create a default definition
535
                if ($generator->getDefinition()) {
536
                    break;
537
                }
538
539
                // @todo guilhermeblanco Move sequence generation to DBAL
540
                $sequencePrefix = $platform->getSequencePrefix($field->getTableName(), $field->getSchemaName());
0 ignored issues
show
Bug introduced by
The method getSchemaName() does not seem to exist on object<Doctrine\ORM\Mapping\FieldMetadata>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
541
                $idSequenceName = sprintf('%s_%s_seq', $sequencePrefix, $field->getColumnName());
542
                $sequenceName   = $platform->fixSchemaElementName($idSequenceName);
543
544
                $field->setValueGenerator(
545
                    new ValueGeneratorMetadata(
546
                        $generator->getType(),
547
                        [
548
                            'sequenceName'   => $sequenceName,
549
                            'allocationSize' => 1,
550
                        ]
551
                    )
552
                );
553
554
                break;
555
556
            case GeneratorType::TABLE:
557
                throw new ORMException("TableGenerator not yet implemented.");
558
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
559
560
            case GeneratorType::CUSTOM:
561
                $definition = $generator->getDefinition();
562 107
                if ( ! isset($definition['class'])) {
563
                    throw new ORMException(sprintf('Cannot instantiate custom generator, no class has been defined'));
564 107
                }
565 74
                if ( ! class_exists($definition['class'])) {
566
                    throw new ORMException(sprintf('Cannot instantiate custom generator : %s', var_export($definition, true))); //$definition['class']));
0 ignored issues
show
Unused Code Comprehensibility introduced by
100% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
567
                }
568 39
569 39
                break;
570 38
571
            case GeneratorType::IDENTITY:
572
            case GeneratorType::NONE:
573 1
            case GeneratorType::UUID:
574 1
                break;
575
576
            default:
577
                throw new ORMException("Unknown generator type: " . $generator->getType());
578 1
        }
579
    }
580
581 39
    /**
582
     * {@inheritDoc}
583
     */
584
    protected function getFqcnFromAlias($namespaceAlias, $simpleClassName) : string
585
    {
586
        return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
587
    }
588
589
    /**
590
     * {@inheritDoc}
591
     */
592
    protected function getDriver() : Driver\MappingDriver
593 1
    {
594
        return $this->driver;
595 1
    }
596 1
597 1
    /**
598
     * {@inheritDoc}
599
     */
600 1
    protected function isEntity(ClassMetadata $class) : bool
601 1
    {
602 1
        return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false;
603
    }
604
605 1
    /**
606
     * @return Platforms\AbstractPlatform
607
     */
608
    private function getTargetPlatform() : Platforms\AbstractPlatform
609
    {
610
        if (!$this->targetPlatform) {
611
            $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
612
        }
613
614
        return $this->targetPlatform;
615
    }
616
617 8
    /**
618
     * @param ClassMetadata $class
619 8
     *
620 8
     * @return void
621 4
     */
622
    private function buildValueGenerationPlan(ClassMetadata $class) : void
623
    {
624 8
        /** @var LocalColumnMetadata[] $generatedProperties */
625 8
        $generatedProperties = [];
626 8
627 8
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
628 8
            if (! ($property instanceof LocalColumnMetadata && $property->hasValueGenerator())) {
629 8
                continue;
630
            }
631
632 8
            $generatedProperties[] = $property;
633
        }
634
635
        switch (count($generatedProperties)) {
636
            case 0:
637
                $class->setValueGenerationPlan(new NoopValueGenerationPlan());
638
                break;
639
640
            case 1:
641
                $property  = reset($generatedProperties);
642
                $executor  = new ColumnValueGeneratorExecutor($property, $this->createPropertyValueGenerator($class, $property));
0 ignored issues
show
Security Bug introduced by
It seems like $property defined by reset($generatedProperties) on line 641 can also be of type false; however, Doctrine\ORM\Mapping\Cla...ropertyValueGenerator() does only seem to accept object<Doctrine\ORM\Mapping\LocalColumnMetadata>, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
Security Bug introduced by
It seems like $property defined by reset($generatedProperties) on line 641 can also be of type false; however, Doctrine\ORM\Sequencing\...Executor::__construct() does only seem to accept object<Doctrine\ORM\Mapping\LocalColumnMetadata>, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
643
644 8
                $class->setValueGenerationPlan(new SingleValueGenerationPlan($class, $executor));
645
                break;
646 8
647 8
            default:
648 4
                $executors = [];
649
650
                foreach ($generatedProperties as $property) {
651 8
                    $executors[] = new ColumnValueGeneratorExecutor($property, $this->createPropertyValueGenerator($class, $property));
652
                }
653 8
654 8
                $class->setValueGenerationPlan(new CompositeValueGenerationPlan($class, $executors));
655 8
                break;
656 8
        }
657 8
    }
658 8
659
    /**
660
     * @param ClassMetadata $class
661
     * @param LocalColumnMetadata $property
662 8
     *
663 8
     * @return Sequencing\Generator
664 8
     */
665 8
    private function createPropertyValueGenerator(
666
        ClassMetadata $class,
667
        LocalColumnMetadata $property
668 8
    ) : Sequencing\Generator
669
    {
670
        $platform = $this->getTargetPlatform();
671
672
        switch ($property->getValueGenerator()->getType()) {
673
            case GeneratorType::IDENTITY:
674
                $sequenceName = null;
675
676
                // Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour.
677
                if ($platform->usesSequenceEmulatedIdentityColumns()) {
678
                    $sequencePrefix = $platform->getSequencePrefix($class->getTableName(), $class->getSchemaName());
679
                    $idSequenceName = $platform->getIdentitySequenceName($sequencePrefix, $property->getColumnName());
680 389
                    $sequenceName   = $platform->quoteIdentifier($platform->fixSchemaElementName($idSequenceName));
681
                }
682 389
683 389
                return $property->getTypeName() === 'bigint'
684
                    ? new Sequencing\BigIntegerIdentityGenerator($sequenceName)
685 389
                    : new Sequencing\IdentityGenerator($sequenceName);
686 282
687
            case GeneratorType::SEQUENCE:
688 282
                $definition = $property->getValueGenerator()->getDefinition();
689 282
                return new Sequencing\SequenceGenerator(
690 282
                    $platform->quoteIdentifier($definition['sequenceName']),
691
                    $definition['allocationSize']
692
                );
693 282
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
694
695
            case GeneratorType::UUID:
696
                return new Sequencing\UuidGenerator();
697 389
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
698 389
699 290
            case GeneratorType::CUSTOM:
700 290
                $class = $property->getValueGenerator()->getDefinition()['class'];
701
                return new $class();
702
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
703 290
        }
704
    }
705
}
706