Test Setup Failed
Push — develop ( 6f26e1...ba9041 )
by Guilherme
63:58
created

ClassMetadataFactory::addNestedEmbeddedClasses()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 22
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 10
cts 10
cp 1
rs 9.2
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 3
crap 1

1 Method

Rating   Name   Duplication   Size   Complexity  
A ClassMetadataFactory::addInheritedNamedNativeQueries() 0 17 3
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\Reflection\ReflectionService;
14
use Doctrine\ORM\Sequencing;
15
use Doctrine\ORM\Sequencing\Planning\ColumnValueGeneratorExecutor;
16
use Doctrine\ORM\Sequencing\Planning\CompositeValueGenerationPlan;
17
use Doctrine\ORM\Sequencing\Planning\NoopValueGenerationPlan;
18
use Doctrine\ORM\Sequencing\Planning\SingleValueGenerationPlan;
19
use ReflectionException;
20
21
/**
22
 * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
23
 * metadata mapping information of a class which describes how a class should be mapped
24
 * to a relational database.
25
 *
26
 * @since   2.0
27
 * @author  Benjamin Eberlei <[email protected]>
28
 * @author  Guilherme Blanco <[email protected]>
29
 * @author  Jonathan Wage <[email protected]>
30
 * @author  Roman Borschel <[email protected]>
31
 */
32
class ClassMetadataFactory extends AbstractClassMetadataFactory
33
{
34
    /**
35
     * @var EntityManagerInterface|null
36
     */
37
    private $em;
38
39
    /**
40
     * @var \Doctrine\DBAL\Platforms\AbstractPlatform
41
     */
42
    private $targetPlatform;
43
44
    /**
45
     * @var Driver\MappingDriver
46
     */
47
    private $driver;
48
49
    /**
50
     * @var \Doctrine\Common\EventManager
51
     */
52
    private $evm;
53
54
    /**
55
     * {@inheritdoc}
56
     */
57
    protected function loadMetadata(string $name, ClassMetadataBuildingContext $metadataBuildingContext) : array
58
    {
59
        $loaded = parent::loadMetadata($name, $metadataBuildingContext);
60
61
        array_map([$this, 'resolveDiscriminatorValue'], array_map([$this, 'getMetadataFor'], $loaded));
62
63
        return $loaded;
64
    }
65
66
    /**
67
     * @param EntityManagerInterface $em
68
     */
69
    public function setEntityManager(EntityManagerInterface $em)
70
    {
71
        $this->em = $em;
72
    }
73
74
    /**
75 397
     * {@inheritdoc}
76
     *
77 397
     * @throws ORMException
78
     */
79 380
    protected function initialize() : void
80
    {
81 380
        $this->driver = $this->em->getConfiguration()->getMetadataDriverImpl();
82
        $this->evm = $this->em->getEventManager();
83
        $this->initialized = true;
84
    }
85
86
    /**
87 2292
     * {@inheritdoc}
88
     */
89 2292
    protected function onNotFoundMetadata(
90 2292
        string $className,
91
        ClassMetadataBuildingContext $metadataBuildingContext
92
    ) : ?ClassMetadata
93
    {
94
        if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {
95 465
            return null;
96
        }
97 465
98 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...
99 465
100 465
        $this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs);
101
102
        return $eventArgs->getFoundMetadata();
103
    }
104
105 2
    /**
106
     * {@inheritdoc}
107 2
     *
108
     * @throws MappingException
109
     * @throws ORMException
110
     */
111 2
    protected function doLoadMetadata(
112
        ClassMetadata $class,
113 2
        ClassMetadataBuildingContext $metadataBuildingContext
114
    ) : void
115 2
    {
116
117
        /* @var $class ClassMetadata */
118
        /* @var $parent ClassMetadata|null */
119
        $parent = $class->getParent();
120
121 395
        if ($parent) {
122
            if ($parent->inheritanceType === InheritanceType::SINGLE_TABLE) {
123
                $class->setTable($parent->table);
124
            }
125 395
126 108
            $this->addInheritedProperties($class, $parent);
127 29
128
            $class->setInheritanceType($parent->inheritanceType);
129
            $class->setIdentifier($parent->identifier);
130 108
131 108
            if ($parent->discriminatorColumn) {
132 107
                $class->setDiscriminatorColumn($parent->discriminatorColumn);
133
                $class->setDiscriminatorMap($parent->discriminatorMap);
134 107
            }
135 107
136 107
            $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
137
            $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
138 107
139 4
            if ($parent->isMappedSuperclass && ! $class->getCustomRepositoryClassName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class->getCustomRepositoryClassName() of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
140
                $class->setCustomRepositoryClassName($parent->getCustomRepositoryClassName());
141
            }
142 107
143 65
        }
144 65
145
        // Invoke driver
146
        try {
147 107
            $this->driver->loadMetadataForClass($class->getClassName(), $class, $metadataBuildingContext);
148 107
        } catch (ReflectionException $e) {
149
            throw MappingException::reflectionFailure($class->getClassName(), $e);
150 107
        }
151 1
152
        $this->completeIdentifierGeneratorMappings($class);
153
154 107
        if ($parent) {
155 39
            if ($parent->inheritanceType === InheritanceType::SINGLE_TABLE) {
156
                $class->setTable($parent->table);
157
            }
158
159
            $this->addInheritedIndexes($class, $parent);
160
            $this->addInheritedNamedQueries($class, $parent);
161 395
162 2
            if ($parent->getCache()) {
163
                $class->setCache(clone $parent->getCache());
164
            }
165
166
            if ( ! empty($parent->namedNativeQueries)) {
167
                $this->addInheritedNamedNativeQueries($class, $parent);
168
            }
169 393
170 74
            if ( ! empty($parent->sqlResultSetMappings)) {
171
                $this->addInheritedSqlResultSetMappings($class, $parent);
172 389
            }
173
174
            if ( ! empty($parent->entityListeners) && empty($class->entityListeners)) {
175
                $class->entityListeners = $parent->entityListeners;
176
            }
177
        }
178
179
        if (! $class->discriminatorMap && $class->inheritanceType !== InheritanceType::NONE && $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...
180
            $this->addDefaultDiscriminatorMap($class);
181
        }
182
183
        $this->completeRuntimeMetadata($class, $parent);
184
185
        if ($this->evm->hasListeners(Events::loadClassMetadata)) {
186
            $eventArgs = new LoadClassMetadataEventArgs($class, $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...
187
188
            $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
189
        }
190
191
        $this->buildValueGenerationPlan($class);
192
        $this->validateRuntimeMetadata($class, $parent);
193
    }
194
195
    /**
196
     * @param ClassMetadata      $class
197
     * @param ClassMetadata|null $parent
198
     *
199
     * @return void
200
     */
201
    protected function completeRuntimeMetadata(ClassMetadata $class, ClassMetadata $parent = null) : void
202
    {
203
        if ( ! $parent) {
204
            return;
205
        }
206
207
        if ( ! $parent->isMappedSuperclass) {
208
            return;
209 391
        }
210 107
211 29
        if ($class->isMappedSuperclass) {
212
            return;
213
        }
214 107
215
        $tableName = $class->getTableName();
216 107
217 3
        // Resolve column table names
218
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
219
            if ($property instanceof FieldMetadata) {
220 107
                $property->setTableName($property->getTableName() ?? $tableName);
221
222
                continue;
223
            }
224 107
225 1
            if (! ($property instanceof ToOneAssociationMetadata)) {
226
                continue;
227
            }
228 107
229 8
            // Resolve association join column table names
230
            foreach ($property->getJoinColumns() as $joinColumn) {
231
                /** @var JoinColumnMetadata $joinColumn */
232 107
                $joinColumn->setTableName($joinColumn->getTableName() ?? $tableName);
233 8
            }
234
        }
235
    }
236 107
237 11
    /**
238
     * Validate runtime metadata is correctly defined.
239
     *
240
     * @param ClassMetadata      $class
241 391
     * @param ClassMetadata|null $parent
242
     *
243 391
     * @return void
244 1
     *
245
     * @throws MappingException
246
     */
247 391
    protected function validateRuntimeMetadata(ClassMetadata $class, ClassMetadata $parent = null) : void
248
    {
249 391
        if (! $class->getReflectionClass()) {
250 6
            // only validate if there is a reflection class instance
251
            return;
252 6
        }
253
254
        $class->validateIdentifier();
255 390
        $class->validateAssociations();
256 385
        $class->validateLifecycleCallbacks($this->getReflectionService());
257
258
        // verify inheritance
259
        if ( ! $class->isMappedSuperclass && $class->inheritanceType !== InheritanceType::NONE) {
260
            if ( ! $parent) {
261
                if (count($class->discriminatorMap) === 0) {
262
                    throw MappingException::missingDiscriminatorMap($class->getClassName());
263
                }
264 391
265
                if ( ! $class->discriminatorColumn) {
266 391
                    throw MappingException::missingDiscriminatorColumn($class->getClassName());
267 387
                }
268
            }
269
        } else if ($class->isMappedSuperclass && $class->isRootEntity() && (count($class->discriminatorMap) || $class->discriminatorColumn)) {
270 107
            // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy
271 74
            throw MappingException::noInheritanceOnMappedSuperClass($class->getClassName());
272
        }
273
    }
274 39
275 1
    /**
276
     * {@inheritdoc}
277
     */
278 39
    protected function newClassMetadataInstance(
279
        string $className,
280
        ClassMetadataBuildingContext $metadataBuildingContext
281 39
    ) : ClassMetadata
282 39
    {
283
        return new ClassMetadata($className, $metadataBuildingContext);
284
    }
285
286 39
    /**
287 13
     * {@inheritdoc}
288 10
     */
289 13
    protected function newClassMetadataBuildingContext() : ClassMetadataBuildingContext
290
    {
291
        return new ClassMetadataBuildingContext($this, $this->em->getConfiguration()->getNamingStrategy());
292
    }
293
294
    /**
295
     * Populates the discriminator value of the given metadata (if not set) by iterating over discriminator
296
     * map classes and looking for a fitting one.
297
     *
298
     * @param ClassMetadata $metadata
299
     *
300 39
     * @return void
301
     *
302
     * @throws \InvalidArgumentException
303
     * @throws \ReflectionException
304
     * @throws MappingException
305
     */
306
    private function resolveDiscriminatorValue(ClassMetadata $metadata) : void
307
    {
308
        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...
309
            ! $metadata->getReflectionClass() || $metadata->getReflectionClass()->isAbstract()) {
310
            return;
311
        }
312 390
313
        // minor optimization: avoid loading related metadata when not needed
314 390
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
315
            if ($discriminatorClass === $metadata->getClassName()) {
316 5
                $metadata->discriminatorValue = $discriminatorValue;
317
318
                return;
319 385
            }
320 381
        }
321 381
322
        // iterate over discriminator mappings and resolve actual referenced classes according to existing metadata
323
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
324 381
            if ($metadata->getClassName() === $this->getMetadataFor($discriminatorClass)->getClassName()) {
325 67
                $metadata->discriminatorValue = $discriminatorValue;
326 63
327
                return;
328
            }
329
        }
330 63
331 67
        throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->getClassName(), $metadata->getRootClassName());
332
    }
333
334 349
    /**
335
     * Adds a default discriminator map if no one is given
336 1
     *
337
     * If an entity is of any inheritance type and does not contain a
338 380
     * discriminator map, then the map is generated automatically. This process
339
     * is expensive computation wise.
340
     *
341
     * The automatically generated discriminator map contains the lowercase short name of
342
     * each class as key.
343 390
     *
344
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
345 390
     *
346
     * @throws MappingException
347
     */
348
    private function addDefaultDiscriminatorMap(ClassMetadata $class) : void
349
    {
350
        $allClasses = $this->driver->getAllClassNames();
351
        $fqcn       = $class->getClassName();
352
        $map        = [$this->getShortName($fqcn) => $fqcn];
353
        $duplicates = [];
354
355
        foreach ($allClasses as $subClassCandidate) {
356
            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...
357
                $shortName = $this->getShortName($subClassCandidate);
358 380
359
                if (isset($map[$shortName])) {
360 380
                    $duplicates[] = $shortName;
361 380
                }
362 380
363
                $map[$shortName] = $class->fullyQualifiedClassName($subClassCandidate);
364
            }
365
        }
366 5
367 5
        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...
368 3
            throw MappingException::duplicateDiscriminatorEntry($class->getClassName(), $duplicates, $map);
369
        }
370 5
371
        $class->setDiscriminatorMap($map);
372
    }
373
374
    /**
375 2
     * Gets the lower-case short name of a class.
376 2
     *
377 1
     * @param string $className
378
     *
379 2
     * @return string
380
     */
381
    private function getShortName($className) : string
382
    {
383 1
        if (strpos($className, "\\") === false) {
384
            return strtolower($className);
385
        }
386
387
        $parts = explode("\\", $className);
388
389
        return strtolower(end($parts));
390
    }
391
392
    /**
393
     * Adds inherited fields to the subclass mapping.
394
     *
395
     * @param ClassMetadata $subClass
396
     * @param ClassMetadata $parentClass
397
     *
398
     * @return void
399
     *
400 1
     * @throws MappingException
401
     */
402 1
    private function addInheritedProperties(ClassMetadata $subClass, ClassMetadata $parentClass) : void
403 1
    {
404 1
        $isAbstract = $parentClass->isMappedSuperclass;
405 1
406
        foreach ($parentClass->getDeclaredPropertiesIterator() as $fieldName => $property) {
407 1
            if ($isAbstract && $property instanceof ToManyAssociationMetadata && ! $property->isOwningSide()) {
408 1
                throw MappingException::illegalToManyAssociationOnMappedSuperclass($parentClass->getClassName(), $fieldName);
409 1
            }
410
411 1
            $subClass->addInheritedProperty($property);
412
        }
413
    }
414
415 1
    /**
416
     * Copy the table indices from the parent class superclass to the child class
417
     *
418
     * @param ClassMetadata $subClass
419 1
     * @param ClassMetadata $parentClass
420
     *
421
     * @return void
422
     */
423 1
    private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass) : void
424 1
    {
425
        if ( ! $parentClass->isMappedSuperclass) {
426
            return;
427
        }
428
429
        foreach ($parentClass->table->getIndexes() as $indexName => $index) {
430
            if ($subClass->table->hasIndex($indexName)) {
431
                continue;
432
            }
433 1
434
            $subClass->table->addIndex($index);
435 1
        }
436
437
        foreach ($parentClass->table->getUniqueConstraints() as $constraintName => $constraint) {
438
            if ($subClass->table->hasUniqueConstraint($constraintName)) {
439 1
                continue;
440
            }
441 1
442
            $subClass->table->addUniqueConstraint($constraint);
443
        }
444
    }
445
446
    /**
447
     * Adds inherited named queries to the subclass mapping.
448
     *
449
     * @since 2.2
450
     *
451
     * @param ClassMetadata $subClass
452 108
     * @param ClassMetadata $parentClass
453
     *
454 108
     * @return void
455 92
     *
456
     * @throws MappingException
457
     */
458 108
    private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass) : void
459 93
    {
460
        foreach ($parentClass->getNamedQueries() as $name => $query) {
461 108
            if ($subClass->hasNamedQuery($name)) {
462
                continue;
463
            }
464
465
            $subClass->addNamedQuery($name, $query);
466
        }
467
    }
468
469
    /**
470
     * Adds inherited named native queries to the subclass mapping.
471
     *
472
     * @since 2.3
473 108
     *
474
     * @param ClassMetadata $subClass
475 108
     * @param ClassMetadata $parentClass
476
     *
477 108
     * @return void
478 40
     *
479 6
     * @throws MappingException
480 1
     */
481
    private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass) : void
482
    {
483 5
        foreach ($parentClass->namedNativeQueries as $name => $query) {
484
            if (isset($subClass->namedNativeQueries[$name])) {
485
                continue;
486
            }
487 39
488 34
            $subClass->addNamedNativeQuery(
489 34
                $name,
490 34
                $query['query'],
491
                [
492
                    'resultSetMapping' => $query['resultSetMapping'],
493
                    'resultClass'      => $query['resultClass'],
494
                ]
495 39
            );
496 34
        }
497
    }
498
499 39
    /**
500
     * Adds inherited sql result set mappings to the subclass mapping.
501 107
     *
502
     * @since 2.3
503
     *
504
     * @param ClassMetadata $subClass
505
     * @param ClassMetadata $parentClass
506
     *
507
     * @return void
508
     *
509
     * @throws MappingException
510
     */
511 107
    private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass) : void
512
    {
513
        foreach ($parentClass->sqlResultSetMappings as $name => $mapping) {
514
            if (isset ($subClass->sqlResultSetMappings[$name])) {
515
                continue;
516
            }
517
518
            $entities = [];
519
520
            foreach ($mapping['entities'] as $entity) {
521
                $entities[] = [
522
                    'fields'              => $entity['fields'],
523
                    'discriminatorColumn' => $entity['discriminatorColumn'],
524 107
                    'entityClass'         => $entity['entityClass'],
525
                ];
526
            }
527
528
            $subClass->addSqlResultSetMapping(
529
                [
530
                    'name'     => $mapping['name'],
531
                    'columns'  => $mapping['columns'],
532
                    'entities' => $entities,
533
                ]
534
            );
535
        }
536
    }
537
538
    /**
539
     * Completes the ID generator mapping. If "auto" is specified we choose the generator
540
     * most appropriate for the targeted database platform.
541
     *
542
     * @param ClassMetadata $class
543
     *
544
     * @return void
545
     *
546
     * @throws ORMException
547
     */
548
    private function completeIdentifierGeneratorMappings(ClassMetadata $class) : void
549
    {
550
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
551
            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...
552
                continue;
553
            }
554
555
            $this->completeFieldIdentifierGeneratorMapping($property);
556
        }
557
    }
558
559
    private function completeFieldIdentifierGeneratorMapping(FieldMetadata $field)
560
    {
561
        if (!$field->hasValueGenerator()) {
562 107
            return;
563
        }
564 107
565 74
        $platform  = $this->getTargetPlatform();
566
        $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...
567
        $generator = $field->getValueGenerator();
568 39
569 39
        if ($generator->getType() === GeneratorType::AUTO) {
570 38
            $generator = new ValueGeneratorMetadata(
571
                $platform->prefersSequences()
572
                    ? GeneratorType::SEQUENCE
573 1
                    : ($platform->prefersIdentityColumns()
574 1
                        ? GeneratorType::IDENTITY
575
                        : GeneratorType::TABLE
576
                ),
577
                $field->getValueGenerator()->getDefinition()
578 1
            );
579
            $field->setValueGenerator($generator);
580
        }
581 39
582
        // Validate generator definition and set defaults where needed
583
        switch ($generator->getType()) {
584
            case GeneratorType::SEQUENCE:
585
                // If there is no sequence definition yet, create a default definition
586
                if ($generator->getDefinition()) {
587
                    break;
588
                }
589
590
                // @todo guilhermeblanco Move sequence generation to DBAL
591
                $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...
592
                $idSequenceName = sprintf('%s_%s_seq', $sequencePrefix, $field->getColumnName());
593 1
                $sequenceName   = $platform->fixSchemaElementName($idSequenceName);
594
595 1
                $field->setValueGenerator(
596 1
                    new ValueGeneratorMetadata(
597 1
                        $generator->getType(),
598
                        [
599
                            'sequenceName'   => $sequenceName,
600 1
                            'allocationSize' => 1,
601 1
                        ]
602 1
                    )
603
                );
604
605 1
                break;
606
607
            case GeneratorType::TABLE:
608
                throw new ORMException("TableGenerator not yet implemented.");
609
                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...
610
611
            case GeneratorType::CUSTOM:
612
                $definition = $generator->getDefinition();
613
                if ( ! isset($definition['class'])) {
614
                    throw new ORMException(sprintf('Cannot instantiate custom generator, no class has been defined'));
615
                }
616
                if ( ! class_exists($definition['class'])) {
617 8
                    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...
618
                }
619 8
620 8
                break;
621 4
622
            case GeneratorType::IDENTITY:
623
            case GeneratorType::NONE:
624 8
            case GeneratorType::UUID:
625 8
                break;
626 8
627 8
            default:
628 8
                throw new ORMException("Unknown generator type: " . $generator->getType());
629 8
        }
630
    }
631
632 8
    /**
633
     * {@inheritDoc}
634
     */
635
    protected function wakeupReflection(ClassMetadata $class, ReflectionService $reflService) : void
636
    {
637
        $class->wakeupReflection($reflService);
638
    }
639
640
    /**
641
     * {@inheritDoc}
642
     */
643
    protected function initializeReflection(ClassMetadata $class, ReflectionService $reflService) : void
644 8
    {
645
        $class->initializeReflection($reflService);
646 8
    }
647 8
648 4
    /**
649
     * {@inheritDoc}
650
     */
651 8
    protected function getFqcnFromAlias($namespaceAlias, $simpleClassName) : string
652
    {
653 8
        return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
654 8
    }
655 8
656 8
    /**
657 8
     * {@inheritDoc}
658 8
     */
659
    protected function getDriver() : Driver\MappingDriver
660
    {
661
        return $this->driver;
662 8
    }
663 8
664 8
    /**
665 8
     * {@inheritDoc}
666
     */
667
    protected function isEntity(ClassMetadata $class) : bool
668 8
    {
669
        return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false;
670
    }
671
672
    /**
673
     * @return Platforms\AbstractPlatform
674
     */
675
    private function getTargetPlatform() : Platforms\AbstractPlatform
676
    {
677
        if (!$this->targetPlatform) {
678
            $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
679
        }
680 389
681
        return $this->targetPlatform;
682 389
    }
683 389
684
    /**
685 389
     * @param ClassMetadata $class
686 282
     *
687
     * @return void
688 282
     */
689 282
    private function buildValueGenerationPlan(ClassMetadata $class) : void
690 282
    {
691
        /** @var LocalColumnMetadata[] $generatedProperties */
692
        $generatedProperties = [];
693 282
694
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
695
            if (! ($property instanceof LocalColumnMetadata && $property->hasValueGenerator())) {
696
                continue;
697 389
            }
698 389
699 290
            $generatedProperties[] = $property;
700 290
        }
701
702
        switch (count($generatedProperties)) {
703 290
            case 0:
704
                $class->setValueGenerationPlan(new NoopValueGenerationPlan());
705
                break;
706
707
            case 1:
708
                $property  = reset($generatedProperties);
709
                $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 708 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 708 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...
710 290
711 1
                $class->setValueGenerationPlan(new SingleValueGenerationPlan($class, $executor));
712 290
                break;
713
714
            default:
715 290
                $executors = [];
716
717 290
                foreach ($generatedProperties as $property) {
718
                    $executors[] = new ColumnValueGeneratorExecutor($property, $this->createPropertyValueGenerator($class, $property));
719 143
                }
720
721 6
                $class->setValueGenerationPlan(new CompositeValueGenerationPlan($class, $executors));
722
                break;
723 6
        }
724
    }
725
726
    /**
727
     * @param ClassMetadata $class
728
     * @param LocalColumnMetadata $property
729
     *
730
     * @return Sequencing\Generator
731
     */
732
    private function createPropertyValueGenerator(
733
        ClassMetadata $class,
734
        LocalColumnMetadata $property
735 6
    ) : Sequencing\Generator
736 6
    {
737
        $platform = $this->getTargetPlatform();
738 6
739 6
        switch ($property->getValueGenerator()->getType()) {
740
            case GeneratorType::IDENTITY:
741 140
                $sequenceName = null;
742 134
743 134
                // Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour.
744
                if ($platform->usesSequenceEmulatedIdentityColumns()) {
745 6
                    $sequencePrefix = $platform->getSequencePrefix($class->getTableName(), $class->getSchemaName());
746 2
                    $idSequenceName = $platform->getIdentitySequenceName($sequencePrefix, $property->getColumnName());
747 2
                    $sequenceName   = $platform->quoteIdentifier($platform->fixSchemaElementName($idSequenceName));
748
                }
749 4
750
                return $property->getTypeName() === 'bigint'
751
                    ? new Sequencing\BigIntegerIdentityGenerator($sequenceName)
752
                    : new Sequencing\IdentityGenerator($sequenceName);
753 4
754 4
            case GeneratorType::SEQUENCE:
755
                $definition = $property->getValueGenerator()->getDefinition();
756 4
                return new Sequencing\SequenceGenerator(
757 2
                    $platform->quoteIdentifier($definition['sequenceName']),
758
                    $definition['allocationSize']
759
                );
760 2
                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...
761 2
762
            case GeneratorType::UUID:
763
                return new Sequencing\UuidGenerator();
764
                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...
765
766 387
            case GeneratorType::CUSTOM:
767
                $class = $property->getValueGenerator()->getDefinition()['class'];
768
                return new $class();
769
                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...
770
        }
771
    }
772
}
773