Test Setup Failed
Push — develop ( 082d66...6f26e1 )
by Guilherme
63:04
created

ClassMetadataFactory::initializeReflection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
crap 2
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
     * @var array
56
     */
57
    private $embeddablesActiveNesting = [];
58
59
    /**
60
     * {@inheritDoc}
61
     */
62
    protected function loadMetadata(string $name, ClassMetadataBuildingContext $metadataBuildingContext)
63
    {
64
        $loaded = parent::loadMetadata($name, $metadataBuildingContext);
65
66
        array_map([$this, 'resolveDiscriminatorValue'], array_map([$this, 'getMetadataFor'], $loaded));
67
68
        return $loaded;
69
    }
70
71
    /**
72
     * @param EntityManagerInterface $em
73
     */
74
    public function setEntityManager(EntityManagerInterface $em)
75 397
    {
76
        $this->em = $em;
77 397
    }
78
79 380
    /**
80
     * {@inheritDoc}
81 380
     */
82
    protected function initialize()
83
    {
84
        $this->driver = $this->em->getConfiguration()->getMetadataDriverImpl();
85
        $this->evm = $this->em->getEventManager();
86
        $this->initialized = true;
87 2292
    }
88
89 2292
    /**
90 2292
     * {@inheritDoc}
91
     */
92
    protected function onNotFoundMetadata($className, ClassMetadataBuildingContext $metadataBuildingContext)
93
    {
94
        if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {
95 465
            return;
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
    protected function doLoadMetadata(
109
        ClassMetadata $class,
110
        ClassMetadataBuildingContext $metadataBuildingContext,
111 2
        bool $rootEntityFound
112
    ) : void
113 2
    {
114
115 2
        /* @var $class ClassMetadata */
116
        /* @var $parent ClassMetadata|null */
117
        $parent = $class->getParent();
118
119
        if ($parent) {
120
            if ($parent->inheritanceType === InheritanceType::SINGLE_TABLE) {
121 395
                $class->setTable($parent->table);
122
            }
123
124
            $this->addInheritedProperties($class, $parent);
125 395
            $this->addInheritedEmbeddedClasses($class, $parent);
0 ignored issues
show
Unused Code introduced by
The call to the method Doctrine\ORM\Mapping\Cla...eritedEmbeddedClasses() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
126 108
127 29
            $class->setInheritanceType($parent->inheritanceType);
128
            $class->setIdentifier($parent->identifier);
129
130 108
            if ($parent->discriminatorColumn) {
131 108
                $class->setDiscriminatorColumn($parent->discriminatorColumn);
132 107
                $class->setDiscriminatorMap($parent->discriminatorMap);
133
            }
134 107
135 107
            $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
136 107
            $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
137
138 107
            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...
139 4
                $class->setCustomRepositoryClassName($parent->getCustomRepositoryClassName());
140
            }
141
142 107
        }
143 65
144 65
        // Invoke driver
145
        try {
146
            $this->driver->loadMetadataForClass($class->getClassName(), $class, $metadataBuildingContext);
147 107
        } catch (ReflectionException $e) {
148 107
            throw MappingException::reflectionFailure($class->getClassName(), $e);
149
        }
150 107
151 1
        $this->completeIdentifierGeneratorMappings($class);
152
153
        /*if ( ! $class->isMappedSuperclass) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% 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...
154 107
            foreach ($class->embeddedClasses as $property => $embeddableClass) {
155 39
                if (isset($embeddableClass['inherited'])) {
156
                    continue;
157
                }
158
159
                if ( ! (isset($embeddableClass['class']) && $embeddableClass['class'])) {
160
                    throw MappingException::missingEmbeddedClass($property);
161 395
                }
162 2
163
                if (isset($this->embeddablesActiveNesting[$embeddableClass['class']])) {
164
                    throw MappingException::infiniteEmbeddableNesting($class->getClassName(), $property);
165
                }
166
167
                $this->embeddablesActiveNesting[$class->getClassName()] = true;
168
169 393
                $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']);
170 74
171
                if ($embeddableMetadata->isEmbeddedClass) {
172 389
                    $this->addNestedEmbeddedClasses($embeddableMetadata, $class, $property);
173
                }
174
175
                $identifier = $embeddableMetadata->getIdentifier();
176
177
                if (! empty($identifier)) {
178
                    $this->inheritIdGeneratorMapping($class, $embeddableMetadata);
179
                }
180
181
                $class->inlineEmbeddable($property, $embeddableMetadata);
182
183
                unset($this->embeddablesActiveNesting[$class->getClassName()]);
184
            }
185
        }*/
186
187
        if ($parent) {
188
            if ($parent->inheritanceType === InheritanceType::SINGLE_TABLE) {
189
                $class->setTable($parent->table);
190
            }
191
192
            $this->addInheritedIndexes($class, $parent);
193
            $this->addInheritedNamedQueries($class, $parent);
194
195
            if ($parent->getCache()) {
196
                $class->setCache(clone $parent->getCache());
197
            }
198
199
            if ( ! empty($parent->namedNativeQueries)) {
200
                $this->addInheritedNamedNativeQueries($class, $parent);
201
            }
202
203
            if ( ! empty($parent->sqlResultSetMappings)) {
204
                $this->addInheritedSqlResultSetMappings($class, $parent);
205
            }
206
207
            if ( ! empty($parent->entityListeners) && empty($class->entityListeners)) {
208
                $class->entityListeners = $parent->entityListeners;
209 391
            }
210 107
        }
211 29
212
        if ($class->isRootEntity() && $class->inheritanceType !== InheritanceType::NONE && ! $class->discriminatorMap) {
213
            $this->addDefaultDiscriminatorMap($class);
214 107
        }
215
216 107
        $this->completeRuntimeMetadata($class, $parent);
217 3
218
        if ($this->evm->hasListeners(Events::loadClassMetadata)) {
219
            $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...
220 107
221
            $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
222
        }
223
224 107
        $this->buildValueGenerationPlan($class);
225 1
        $this->validateRuntimeMetadata($class, $parent);
226
    }
227
228 107
    /**
229 8
     * @param ClassMetadata      $class
230
     * @param ClassMetadata|null $parent
231
     *
232 107
     * @return void
233 8
     */
234
    protected function completeRuntimeMetadata(ClassMetadata $class, ClassMetadata $parent = null)
235
    {
236 107
        if ( ! $parent) {
237 11
            return;
238
        }
239
240
        if ( ! $parent->isMappedSuperclass) {
241 391
            return;
242
        }
243 391
244 1
        if ($class->isMappedSuperclass) {
245
            return;
246
        }
247 391
248
        $tableName = $class->getTableName();
249 391
250 6
        // Resolve column table names
251
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
252 6
            if ($property instanceof FieldMetadata) {
253
                $property->setTableName($property->getTableName() ?? $tableName);
254
255 390
                continue;
256 385
            }
257
258
            if (! ($property instanceof ToOneAssociationMetadata)) {
259
                continue;
260
            }
261
262
            // Resolve association join column table names
263
            foreach ($property->getJoinColumns() as $joinColumn) {
264 391
                $joinColumn->setTableName($joinColumn->getTableName() ?? $tableName);
265
            }
266 391
        }
267 387
268
        // Resolve embedded table names
269
        /*foreach ($class->embeddedClasses as &$mapping) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% 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...
270 107
            if ( ! isset($mapping['tableName'])) {
271 74
                $mapping['tableName'] = $mapping['tableName'] ?? $tableName;
272
            }
273
        }*/
274 39
    }
275 1
276
    /**
277
     * Validate runtime metadata is correctly defined.
278 39
     *
279
     * @param ClassMetadata      $class
280
     * @param ClassMetadata|null $parent
281 39
     *
282 39
     * @return void
283
     *
284
     * @throws MappingException
285
     */
286 39
    protected function validateRuntimeMetadata(ClassMetadata $class, ClassMetadata $parent = null)
287 13
    {
288 10
        if (! $class->getReflectionClass()) {
289 13
            // only validate if there is a reflection class instance
290
            return;
291
        }
292
293
        $class->validateIdentifier();
294
        $class->validateAssociations();
295
        $class->validateLifecycleCallbacks($this->getReflectionService());
296
297
        // verify inheritance
298
        if ( ! $class->isMappedSuperclass && $class->inheritanceType !== InheritanceType::NONE) {
299
            if ( ! $parent) {
300 39
                if (count($class->discriminatorMap) === 0) {
301
                    throw MappingException::missingDiscriminatorMap($class->getClassName());
302
                }
303
304
                if ( ! $class->discriminatorColumn) {
305
                    throw MappingException::missingDiscriminatorColumn($class->getClassName());
306
                }
307
            }
308
        } else if ($class->isMappedSuperclass && $class->isRootEntity() && (count($class->discriminatorMap) || $class->discriminatorColumn)) {
309
            // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy
310
            throw MappingException::noInheritanceOnMappedSuperClass($class->getClassName());
311
        }
312 390
    }
313
314 390
    /**
315
     * {@inheritdoc}
316 5
     */
317
    protected function newClassMetadataInstance(
318
        string $className,
319 385
        ClassMetadataBuildingContext $metadataBuildingContext
320 381
    ) : ClassMetadata
321 381
    {
322
        return new ClassMetadata($className, $metadataBuildingContext);
323
    }
324 381
325 67
    /**
326 63
     * {@inheritdoc}
327
     */
328
    protected function newClassMetadataBuildingContext() : ClassMetadataBuildingContext
329
    {
330 63
        return new ClassMetadataBuildingContext($this, $this->em->getConfiguration()->getNamingStrategy());
331 67
    }
332
333
    /**
334 349
     * Populates the discriminator value of the given metadata (if not set) by iterating over discriminator
335
     * map classes and looking for a fitting one.
336 1
     *
337
     * @param ClassMetadata $metadata
338 380
     *
339
     * @return void
340
     *
341
     * @throws MappingException
342
     */
343 390
    private function resolveDiscriminatorValue(ClassMetadata $metadata)
344
    {
345 390
        if ($metadata->discriminatorValue || ! $metadata->discriminatorMap || $metadata->isMappedSuperclass ||
346
            ! $metadata->getReflectionClass() || $metadata->getReflectionClass()->isAbstract()) {
347
            return;
348
        }
349
350
        // minor optimization: avoid loading related metadata when not needed
351
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
352
            if ($discriminatorClass === $metadata->getClassName()) {
353
                $metadata->discriminatorValue = $discriminatorValue;
354
355
                return;
356
            }
357
        }
358 380
359
        // iterate over discriminator mappings and resolve actual referenced classes according to existing metadata
360 380
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
361 380
            if ($metadata->getClassName() === $this->getMetadataFor($discriminatorClass)->getClassName()) {
362 380
                $metadata->discriminatorValue = $discriminatorValue;
363
364
                return;
365
            }
366 5
        }
367 5
368 3
        throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->getClassName(), $metadata->getRootClassName());
369
    }
370 5
371
    /**
372
     * Adds a default discriminator map if no one is given
373
     *
374
     * If an entity is of any inheritance type and does not contain a
375 2
     * discriminator map, then the map is generated automatically. This process
376 2
     * is expensive computation wise.
377 1
     *
378
     * The automatically generated discriminator map contains the lowercase short name of
379 2
     * each class as key.
380
     *
381
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
382
     *
383 1
     * @throws MappingException
384
     */
385
    private function addDefaultDiscriminatorMap(ClassMetadata $class)
386
    {
387
        $allClasses = $this->driver->getAllClassNames();
388
        $fqcn       = $class->getClassName();
389
        $map        = [$this->getShortName($fqcn) => $fqcn];
390
        $duplicates = [];
391
392
        foreach ($allClasses as $subClassCandidate) {
393
            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...
394
                $shortName = $this->getShortName($subClassCandidate);
395
396
                if (isset($map[$shortName])) {
397
                    $duplicates[] = $shortName;
398
                }
399
400 1
                $map[$shortName] = $class->fullyQualifiedClassName($subClassCandidate);
401
            }
402 1
        }
403 1
404 1
        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...
405 1
            throw MappingException::duplicateDiscriminatorEntry($class->getClassName(), $duplicates, $map);
406
        }
407 1
408 1
        $class->setDiscriminatorMap($map);
409 1
    }
410
411 1
    /**
412
     * Gets the lower-case short name of a class.
413
     *
414
     * @param string $className
415 1
     *
416
     * @return string
417
     */
418
    private function getShortName($className)
419 1
    {
420
        if (strpos($className, "\\") === false) {
421
            return strtolower($className);
422
        }
423 1
424 1
        $parts = explode("\\", $className);
425
426
        return strtolower(end($parts));
427
    }
428
429
    /**
430
     * Adds inherited fields to the subclass mapping.
431
     *
432
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
433 1
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
434
     *
435 1
     * @return void
436
     */
437
    private function addInheritedProperties(ClassMetadata $subClass, ClassMetadata $parentClass)
438
    {
439 1
        $isAbstract = $parentClass->isMappedSuperclass;
440
441 1
        foreach ($parentClass->getDeclaredPropertiesIterator() as $fieldName => $property) {
442
            if ($isAbstract && $property instanceof ToManyAssociationMetadata && ! $property->isOwningSide()) {
443
                throw MappingException::illegalToManyAssociationOnMappedSuperclass($parentClass->getClassName(), $fieldName);
444
            }
445
446
            $subClass->addInheritedProperty($property);
447
        }
448
    }
449
450
    /**
451
     * Adds inherited embedded mappings to the subclass mapping.
452 108
     *
453
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
454 108
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
455 92
     *
456
     * @return void
457
     */
458 108
    private function addInheritedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass)
0 ignored issues
show
Unused Code introduced by
The parameter $subClass is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $parentClass is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
459 93
    {
460
        /*foreach ($parentClass->embeddedClasses as $field => $embeddedClass) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
61% 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...
461 108
            if ( ! isset($embeddedClass['tableName'])) {
462
                $embeddedClass['tableName'] = ! $parentClass->isMappedSuperclass ? $parentClass->getTableName() : null;
463
            }
464
465
            if ( ! isset($embeddedClass['inherited']) && ! $parentClass->isMappedSuperclass) {
466
                $embeddedClass['inherited'] = $parentClass->getClassName();
467
            }
468
469
            $subClass->embeddedClasses[$field] = $embeddedClass;
470
        }*/
471
    }
472
473 108
    /**
474
     * Adds nested embedded classes metadata to a parent class.
475 108
     *
476
     * @param ClassMetadata $subClass    Sub embedded class metadata to add nested embedded classes metadata from.
477 108
     * @param ClassMetadata $parentClass Parent class to add nested embedded classes metadata to.
478 40
     * @param string        $prefix      Embedded classes' prefix to use for nested embedded classes field names.
479 6
     */
480 1
    private function addNestedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass, $prefix)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
Unused Code introduced by
The parameter $subClass is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $parentClass is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $prefix is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
481
    {
482
        /*foreach ($subClass->embeddedClasses as $property => $embeddableClass) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% 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...
483 5
            if (isset($embeddableClass['inherited'])) {
484
                continue;
485
            }
486
487 39
            $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']);
488 34
489 34
            $parentClass->mapEmbedded(
490 34
                [
491
                    'fieldName' => $prefix . '.' . $property,
492
                    'class' => $embeddableMetadata->getClassName(),
493
                    'columnPrefix' => $embeddableClass['columnPrefix'],
494
                    'declaredField' => $embeddableClass['declaredField']
495 39
                            ? $prefix . '.' . $embeddableClass['declaredField']
496 34
                            : $prefix,
497
                    'originalField' => $embeddableClass['originalField'] ?: $property,
498
                ]
499 39
            );
500
        }*/
501 107
    }
502
503
    /**
504
     * Copy the table indices from the parent class superclass to the child class
505
     *
506
     * @param ClassMetadata $subClass
507
     * @param ClassMetadata $parentClass
508
     *
509
     * @return void
510
     */
511 107
    private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass)
512
    {
513
        if ( ! $parentClass->isMappedSuperclass) {
514
            return;
515
        }
516
517
        foreach ($parentClass->table->getIndexes() as $indexName => $index) {
518
            if ($subClass->table->hasIndex($indexName)) {
519
                continue;
520
            }
521
522
            $subClass->table->addIndex($index);
523
        }
524 107
525
        foreach ($parentClass->table->getUniqueConstraints() as $constraintName => $constraint) {
526
            if ($subClass->table->hasUniqueConstraint($constraintName)) {
527
                continue;
528
            }
529
530
            $subClass->table->addUniqueConstraint($constraint);
531
        }
532
    }
533
534
    /**
535
     * Adds inherited named queries to the subclass mapping.
536
     *
537
     * @since 2.2
538
     *
539
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
540
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
541
     *
542
     * @return void
543
     */
544
    private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass)
545
    {
546
        foreach ($parentClass->getNamedQueries() as $name => $query) {
547
            if ($subClass->hasNamedQuery($name)) {
548
                continue;
549
            }
550
551
            $subClass->addNamedQuery($name, $query);
552
        }
553
    }
554
555
    /**
556
     * Adds inherited named native queries to the subclass mapping.
557
     *
558
     * @since 2.3
559
     *
560
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
561
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
562 107
     *
563
     * @return void
564 107
     */
565 74
    private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass)
566
    {
567
        foreach ($parentClass->namedNativeQueries as $name => $query) {
568 39
            if (isset($subClass->namedNativeQueries[$name])) {
569 39
                continue;
570 38
            }
571
572
            $subClass->addNamedNativeQuery(
573 1
                $name,
574 1
                $query['query'],
575
                [
576
                    'resultSetMapping' => $query['resultSetMapping'],
577
                    'resultClass'      => $query['resultClass'],
578 1
                ]
579
            );
580
        }
581 39
    }
582
583
    /**
584
     * Adds inherited sql result set mappings to the subclass mapping.
585
     *
586
     * @since 2.3
587
     *
588
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
589
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
590
     *
591
     * @return void
592
     */
593 1
    private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass)
594
    {
595 1
        foreach ($parentClass->sqlResultSetMappings as $name => $mapping) {
596 1
            if (isset ($subClass->sqlResultSetMappings[$name])) {
597 1
                continue;
598
            }
599
600 1
            $entities = [];
601 1
602 1
            foreach ($mapping['entities'] as $entity) {
603
                $entities[] = [
604
                    'fields'              => $entity['fields'],
605 1
                    'discriminatorColumn' => $entity['discriminatorColumn'],
606
                    'entityClass'         => $entity['entityClass'],
607
                ];
608
            }
609
610
            $subClass->addSqlResultSetMapping(
611
                [
612
                    'name'     => $mapping['name'],
613
                    'columns'  => $mapping['columns'],
614
                    'entities' => $entities,
615
                ]
616
            );
617 8
        }
618
    }
619 8
620 8
    /**
621 4
     * Completes the ID generator mapping. If "auto" is specified we choose the generator
622
     * most appropriate for the targeted database platform.
623
     *
624 8
     * @param ClassMetadata $class
625 8
     *
626 8
     * @return void
627 8
     *
628 8
     * @throws ORMException
629 8
     */
630
    private function completeIdentifierGeneratorMappings(ClassMetadata $class)
631
    {
632 8
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
633
            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...
634
                continue;
635
            }
636
637
            $this->completeFieldIdentifierGeneratorMapping($property);
638
        }
639
    }
640
641
    private function completeFieldIdentifierGeneratorMapping(FieldMetadata $field)
642
    {
643
        if (!$field->hasValueGenerator()) {
644 8
            return;
645
        }
646 8
647 8
        $platform  = $this->getTargetPlatform();
648 4
        $class     = $field->getDeclaringClass();
649
        $generator = $field->getValueGenerator();
650
651 8
        if ($generator->getType() === GeneratorType::AUTO) {
652
            $generator = new ValueGeneratorMetadata(
653 8
                $platform->prefersSequences()
654 8
                    ? GeneratorType::SEQUENCE
655 8
                    : ($platform->prefersIdentityColumns()
656 8
                        ? GeneratorType::IDENTITY
657 8
                        : GeneratorType::TABLE
658 8
                ),
659
                $field->getValueGenerator()->getDefinition()
660
            );
661
            $field->setValueGenerator($generator);
662 8
        }
663 8
664 8
        // Validate generator definition and set defaults where needed
665 8
        switch ($generator->getType()) {
666
            case GeneratorType::SEQUENCE:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
667
                // If there is no sequence definition yet, create a default definition
668 8
                if ($generator->getDefinition()) {
669
                    break;
670
                }
671
672
                // @todo guilhermeblanco Move sequence generation to DBAL
673
                $sequencePrefix = $platform->getSequencePrefix($class->getTableName(), $class->getSchemaName());
674
                $idSequenceName = sprintf('%s_%s_seq', $sequencePrefix, $field->getColumnName());
675
                $sequenceName   = $platform->fixSchemaElementName($idSequenceName);
676
677
                $field->setValueGenerator(
678
                    new ValueGeneratorMetadata(
679
                        $generator->getType(),
680 389
                        [
681
                            'sequenceName'   => $sequenceName,
682 389
                            'allocationSize' => 1,
683 389
                        ]
684
                    )
685 389
                );
686 282
687
                break;
688 282
689 282
            case GeneratorType::TABLE:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
690 282
                throw new ORMException("TableGenerator not yet implemented.");
691
                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...
692
693 282
            case GeneratorType::CUSTOM:
694
                $definition = $generator->getDefinition();
695
                if ( ! isset($definition['class'])) {
696
                    throw new ORMException(sprintf('Cannot instantiate custom generator, no class has been defined'));
697 389
                }
698 389
                if ( ! class_exists($definition['class'])) {
699 290
                    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...
700 290
                }
701
702
                break;
703 290
704
            case GeneratorType::IDENTITY:
705
            case GeneratorType::NONE:
706
            case GeneratorType::UUID:
707
                break;
708
709
            default:
710 290
                throw new ORMException("Unknown generator type: " . $generator->getType());
711 1
        }
712 290
    }
713
714
    /**
715 290
     * {@inheritDoc}
716
     */
717 290
    protected function wakeupReflection(ClassMetadata $class, ReflectionService $reflService)
718
    {
719 143
        $class->wakeupReflection($reflService);
720
    }
721 6
722
    /**
723 6
     * {@inheritDoc}
724
     */
725
    protected function initializeReflection(ClassMetadata $class, ReflectionService $reflService)
726
    {
727
        $class->initializeReflection($reflService);
728
    }
729
730
    /**
731
     * {@inheritDoc}
732
     */
733
    protected function getFqcnFromAlias($namespaceAlias, $simpleClassName)
734
    {
735 6
        return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
736 6
    }
737
738 6
    /**
739 6
     * {@inheritDoc}
740
     */
741 140
    protected function getDriver()
742 134
    {
743 134
        return $this->driver;
744
    }
745 6
746 2
    /**
747 2
     * {@inheritDoc}
748
     */
749 4
    protected function isEntity(ClassMetadata $class)
750
    {
751
        return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false;
752
    }
753 4
754 4
    /**
755
     * @return Platforms\AbstractPlatform
756 4
     */
757 2
    private function getTargetPlatform()
758
    {
759
        if (!$this->targetPlatform) {
760 2
            $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
761 2
        }
762
763
        return $this->targetPlatform;
764
    }
765
766 387
    private function buildValueGenerationPlan(ClassMetadata $class): void
767
    {
768
        /** @var LocalColumnMetadata[] $generatedProperties */
769
        $generatedProperties = [];
770
771
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
772
            if (! ($property instanceof LocalColumnMetadata && $property->hasValueGenerator())) {
773
                continue;
774 74
            }
775
776 74
            $generatedProperties[] = $property;
777 3
        }
778 71
779
        switch (count($generatedProperties)) {
780
            case 0:
781
                $class->setValueGenerationPlan(new NoopValueGenerationPlan());
782 74
                break;
783 74
784
            case 1:
785
                $property  = reset($generatedProperties);
786 74
                $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 785 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 785 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...
787 74
788
                $class->setValueGenerationPlan(new SingleValueGenerationPlan($class, $executor));
789 74
                break;
790
791
            default:
792
                $executors = [];
793
794 1985
                foreach ($generatedProperties as $property) {
795
                    $executors[] = new ColumnValueGeneratorExecutor($property, $this->createPropertyValueGenerator($class, $property));
796
                }
797 1985
798 1985
                $class->setValueGenerationPlan(new CompositeValueGenerationPlan($class, $executors));
799
                break;
800
        }
801
    }
802
803 395
    private function createPropertyValueGenerator(ClassMetadata $class, LocalColumnMetadata $property): Sequencing\Generator
804
    {
805
        $platform = $this->getTargetPlatform();
806 395
807 395
        switch ($property->getValueGenerator()->getType()) {
808
            case GeneratorType::IDENTITY:
809
                $sequenceName = null;
810
811
                // Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour.
812 4
                if ($platform->usesSequenceEmulatedIdentityColumns()) {
813
                    $sequencePrefix = $platform->getSequencePrefix($class->getTableName(), $class->getSchemaName());
814 4
                    $idSequenceName = $platform->getIdentitySequenceName($sequencePrefix, $property->getColumnName());
815
                    $sequenceName   = $platform->quoteIdentifier($platform->fixSchemaElementName($idSequenceName));
816
                }
817
818
                return $property->getTypeName() === 'bigint'
819
                    ? new Sequencing\BigIntegerIdentityGenerator($sequenceName)
820 213
                    : new Sequencing\IdentityGenerator($sequenceName);
821
822 213
            case GeneratorType::SEQUENCE:
823
                $definition = $property->getValueGenerator()->getDefinition();
824
                return new Sequencing\SequenceGenerator(
825
                    $platform->quoteIdentifier($definition['sequenceName']),
826
                    $definition['allocationSize']
827
                );
828 385
                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...
829
830 385
            case GeneratorType::UUID:
831
                return new Sequencing\UuidGenerator();
832
                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...
833
834
            case GeneratorType::CUSTOM:
835
                $class = $property->getValueGenerator()->getDefinition()['class'];
836 389
                return new $class();
837
                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...
838 389
        }
839 389
    }
840
}
841