Completed
Push — master ( 22ecc2...971c40 )
by Marco
14:47
created

AnnotationDriver::loadMetadataForClass()   F

Complexity

Conditions 91
Paths > 20000

Size

Total Lines 483
Code Lines 265

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 248
CRAP Score 91.1088

Importance

Changes 0
Metric Value
dl 0
loc 483
c 0
b 0
f 0
ccs 248
cts 254
cp 0.9764
rs 2
cc 91
eloc 265
nc 327715204
nop 2
crap 91.1088

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
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM\Mapping\Driver;
21
22
use Doctrine\Common\Annotations\AnnotationReader;
23
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
24
use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver;
25
use Doctrine\ORM\Events;
26
use Doctrine\ORM\Mapping;
27
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
28
use Doctrine\ORM\Mapping\MappingException;
29
30
/**
31
 * The AnnotationDriver reads the mapping metadata from docblock annotations.
32
 *
33
 * @since 2.0
34
 * @author Benjamin Eberlei <[email protected]>
35
 * @author Guilherme Blanco <[email protected]>
36
 * @author Jonathan H. Wage <[email protected]>
37
 * @author Roman Borschel <[email protected]>
38
 */
39
class AnnotationDriver extends AbstractAnnotationDriver
40
{
41
    /**
42
     * {@inheritDoc}
43
     */
44
    protected $entityAnnotationClasses = [
45
        Mapping\Entity::class => 1,
46
        Mapping\MappedSuperclass::class => 2,
47
    ];
48
49
    /**
50
     * {@inheritDoc}
51
     */
52 358
    public function loadMetadataForClass($className, ClassMetadata $metadata)
53
    {
54
        /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */
55 358
        $class = $metadata->getReflectionClass();
56
57 358
        if ( ! $class) {
58
            // this happens when running annotation driver in combination with
59
            // static reflection services. This is not the nicest fix
60 1
            $class = new \ReflectionClass($metadata->name);
61
        }
62
63 358
        $classAnnotations = $this->reader->getClassAnnotations($class);
64
65 358
        if ($classAnnotations) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $classAnnotations 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...
66 355
            foreach ($classAnnotations as $key => $annot) {
67 355
                if ( ! is_numeric($key)) {
68
                    continue;
69
                }
70
71 355
                $classAnnotations[get_class($annot)] = $annot;
72
            }
73
        }
74
75
        // Evaluate Entity annotation
76 358
        if (isset($classAnnotations[Mapping\Entity::class])) {
77 350
            $entityAnnot = $classAnnotations[Mapping\Entity::class];
78 350
            if ($entityAnnot->repositoryClass !== null) {
79 8
                $metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
80
            }
81
82 350
            if ($entityAnnot->readOnly) {
83 350
                $metadata->markReadOnly();
84
            }
85 43
        } else if (isset($classAnnotations[Mapping\MappedSuperclass::class])) {
86 31
            $mappedSuperclassAnnot = $classAnnotations[Mapping\MappedSuperclass::class];
87
88 31
            $metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass);
89 31
            $metadata->isMappedSuperclass = true;
90 14
        } else if (isset($classAnnotations[Mapping\Embeddable::class])) {
91 11
            $metadata->isEmbeddedClass = true;
92
        } else {
93 3
            throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
94
        }
95
96
        // Evaluate Table annotation
97 355
        if (isset($classAnnotations[Mapping\Table::class])) {
98 187
            $tableAnnot   = $classAnnotations[Mapping\Table::class];
99
            $primaryTable = [
100 187
                'name'   => $tableAnnot->name,
101 187
                'schema' => $tableAnnot->schema
102
            ];
103
104 187
            if ($tableAnnot->indexes !== null) {
105 14
                foreach ($tableAnnot->indexes as $indexAnnot) {
106 14
                    $index = ['columns' => $indexAnnot->columns];
107
108 14
                    if ( ! empty($indexAnnot->flags)) {
109 1
                        $index['flags'] = $indexAnnot->flags;
110
                    }
111
112 14
                    if ( ! empty($indexAnnot->options)) {
113 1
                        $index['options'] = $indexAnnot->options;
114
                    }
115
116 14
                    if ( ! empty($indexAnnot->name)) {
117 13
                        $primaryTable['indexes'][$indexAnnot->name] = $index;
118
                    } else {
119 14
                        $primaryTable['indexes'][] = $index;
120
                    }
121
                }
122
            }
123
124 187
            if ($tableAnnot->uniqueConstraints !== null) {
125 7
                foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) {
126 7
                    $uniqueConstraint = ['columns' => $uniqueConstraintAnnot->columns];
127
128 7
                    if ( ! empty($uniqueConstraintAnnot->options)) {
129 1
                        $uniqueConstraint['options'] = $uniqueConstraintAnnot->options;
130
                    }
131
132 7
                    if ( ! empty($uniqueConstraintAnnot->name)) {
133 7
                        $primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint;
134
                    } else {
135 7
                        $primaryTable['uniqueConstraints'][] = $uniqueConstraint;
136
                    }
137
                }
138
            }
139
140 187
            if ($tableAnnot->options) {
141 4
                $primaryTable['options'] = $tableAnnot->options;
142
            }
143
144 187
            $metadata->setPrimaryTable($primaryTable);
145
        }
146
147
        // Evaluate @Cache annotation
148 355
        if (isset($classAnnotations[Mapping\Cache::class])) {
149 16
            $cacheAnnot = $classAnnotations[Mapping\Cache::class];
150
            $cacheMap   = [
151 16
                'region' => $cacheAnnot->region,
152 16
                'usage'  => constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage),
153
            ];
154
155 16
            $metadata->enableCache($cacheMap);
156
        }
157
158
        // Evaluate NamedNativeQueries annotation
159 355
        if (isset($classAnnotations[Mapping\NamedNativeQueries::class])) {
160 16
            $namedNativeQueriesAnnot = $classAnnotations[Mapping\NamedNativeQueries::class];
161
162 16
            foreach ($namedNativeQueriesAnnot->value as $namedNativeQuery) {
163 16
                $metadata->addNamedNativeQuery(
164
                    [
165 16
                        'name'              => $namedNativeQuery->name,
166 16
                        'query'             => $namedNativeQuery->query,
167 16
                        'resultClass'       => $namedNativeQuery->resultClass,
168 16
                        'resultSetMapping'  => $namedNativeQuery->resultSetMapping,
169
                    ]
170
                );
171
            }
172
        }
173
174
        // Evaluate SqlResultSetMappings annotation
175 355
        if (isset($classAnnotations[Mapping\SqlResultSetMappings::class])) {
176 16
            $sqlResultSetMappingsAnnot = $classAnnotations[Mapping\SqlResultSetMappings::class];
177
178 16
            foreach ($sqlResultSetMappingsAnnot->value as $resultSetMapping) {
179 16
                $entities = [];
180 16
                $columns  = [];
181 16
                foreach ($resultSetMapping->entities as $entityResultAnnot) {
182
                    $entityResult = [
183 16
                        'fields'                => [],
184 16
                        'entityClass'           => $entityResultAnnot->entityClass,
185 16
                        'discriminatorColumn'   => $entityResultAnnot->discriminatorColumn,
186
                    ];
187
188 16
                    foreach ($entityResultAnnot->fields as $fieldResultAnnot) {
189 16
                        $entityResult['fields'][] = [
190 16
                            'name'      => $fieldResultAnnot->name,
191 16
                            'column'    => $fieldResultAnnot->column
192
                        ];
193
                    }
194
195 16
                    $entities[] = $entityResult;
196
                }
197
198 16
                foreach ($resultSetMapping->columns as $columnResultAnnot) {
199 10
                    $columns[] = [
200 10
                        'name' => $columnResultAnnot->name,
201
                    ];
202
                }
203
204 16
                $metadata->addSqlResultSetMapping(
205
                    [
206 16
                        'name'          => $resultSetMapping->name,
207 16
                        'entities'      => $entities,
208 16
                        'columns'       => $columns
209
                    ]
210
                );
211
            }
212
        }
213
214
        // Evaluate NamedQueries annotation
215 355
        if (isset($classAnnotations[Mapping\NamedQueries::class])) {
216 9
            $namedQueriesAnnot = $classAnnotations[Mapping\NamedQueries::class];
217
218 9
            if ( ! is_array($namedQueriesAnnot->value)) {
219
                throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations.");
220
            }
221
222 9
            foreach ($namedQueriesAnnot->value as $namedQuery) {
223 9
                if ( ! ($namedQuery instanceof Mapping\NamedQuery)) {
224
                    throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations.");
225
                }
226 9
                $metadata->addNamedQuery(
227
                    [
228 9
                        'name'  => $namedQuery->name,
229 9
                        'query' => $namedQuery->query
230
                    ]
231
                );
232
            }
233
        }
234
235
        // Evaluate InheritanceType annotation
236 355
        if (isset($classAnnotations[Mapping\InheritanceType::class])) {
237 67
            $inheritanceTypeAnnot = $classAnnotations[Mapping\InheritanceType::class];
238
239 67
            $metadata->setInheritanceType(
240 67
                constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAnnot->value)
241
            );
242
243 67
            if ($metadata->inheritanceType != Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) {
244
                // Evaluate DiscriminatorColumn annotation
245 67
                if (isset($classAnnotations[Mapping\DiscriminatorColumn::class])) {
246 51
                    $discrColumnAnnot = $classAnnotations[Mapping\DiscriminatorColumn::class];
247
248 51
                    $metadata->setDiscriminatorColumn(
249
                        [
250 51
                            'name'             => $discrColumnAnnot->name,
251 51
                            'type'             => $discrColumnAnnot->type ?: 'string',
252 51
                            'length'           => $discrColumnAnnot->length ?: 255,
253 51
                            'columnDefinition' => $discrColumnAnnot->columnDefinition,
254
                        ]
255
                    );
256
                } else {
257 19
                    $metadata->setDiscriminatorColumn(['name' => 'dtype', 'type' => 'string', 'length' => 255]);
258
                }
259
260
                // Evaluate DiscriminatorMap annotation
261 67
                if (isset($classAnnotations[Mapping\DiscriminatorMap::class])) {
262 66
                    $discrMapAnnot = $classAnnotations[Mapping\DiscriminatorMap::class];
263 66
                    $metadata->setDiscriminatorMap($discrMapAnnot->value);
264
                }
265
            }
266
        }
267
268
269
        // Evaluate DoctrineChangeTrackingPolicy annotation
270 355
        if (isset($classAnnotations[Mapping\ChangeTrackingPolicy::class])) {
271 5
            $changeTrackingAnnot = $classAnnotations[Mapping\ChangeTrackingPolicy::class];
272 5
            $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' . $changeTrackingAnnot->value));
273
        }
274
275
        // Evaluate annotations on properties/fields
276
        /* @var $property \ReflectionProperty */
277 355
        foreach ($class->getProperties() as $property) {
278 353
            if ($metadata->isMappedSuperclass && ! $property->isPrivate()
279
                ||
280 353
                $metadata->isInheritedField($property->name)
281
                ||
282 353
                $metadata->isInheritedAssociation($property->name)
283
                ||
284 353
                $metadata->isInheritedEmbeddedClass($property->name)) {
285 70
                continue;
286
            }
287
288 353
            $mapping = [];
289 353
            $mapping['fieldName'] = $property->getName();
290
291
            // Evaluate @Cache annotation
292 353
            if (($cacheAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Cache::class)) !== null) {
293 13
                $mapping['cache'] = $metadata->getAssociationCacheDefaults(
294 13
                    $mapping['fieldName'],
295
                    [
296 13
                        'usage'  => constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage),
297 13
                        'region' => $cacheAnnot->region,
298
                    ]
299
                );
300
            }
301
            // Check for JoinColumn/JoinColumns annotations
302 352
            $joinColumns = [];
303
304 352
            if ($joinColumnAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinColumn::class)) {
305 136
                $joinColumns[] = $this->joinColumnToArray($joinColumnAnnot);
306 351
            } else if ($joinColumnsAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinColumns::class)) {
307 18
                foreach ($joinColumnsAnnot->value as $joinColumn) {
308 18
                    $joinColumns[] = $this->joinColumnToArray($joinColumn);
309
                }
310
            }
311
312
            // Field can only be annotated with one of:
313
            // @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany
314 352
            if ($columnAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Column::class)) {
315 344
                if ($columnAnnot->type == null) {
316
                    throw MappingException::propertyTypeIsRequired($className, $property->getName());
317
                }
318
319 344
                $mapping = $this->columnToArray($property->getName(), $columnAnnot);
320
321 344
                if ($idAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Id::class)) {
0 ignored issues
show
Unused Code introduced by
$idAnnot 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...
322 337
                    $mapping['id'] = true;
323
                }
324
325 344
                if ($generatedValueAnnot = $this->reader->getPropertyAnnotation($property, Mapping\GeneratedValue::class)) {
326 287
                    $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $generatedValueAnnot->strategy));
327
                }
328
329 344
                if ($this->reader->getPropertyAnnotation($property, Mapping\Version::class)) {
330 12
                    $metadata->setVersionMapping($mapping);
331
                }
332
333 344
                $metadata->mapField($mapping);
334
335
                // Check for SequenceGenerator/TableGenerator definition
336 344
                if ($seqGeneratorAnnot = $this->reader->getPropertyAnnotation($property, Mapping\SequenceGenerator::class)) {
337 7
                    $metadata->setSequenceGeneratorDefinition(
338
                        [
339 7
                            'sequenceName' => $seqGeneratorAnnot->sequenceName,
340 7
                            'allocationSize' => $seqGeneratorAnnot->allocationSize,
341 7
                            'initialValue' => $seqGeneratorAnnot->initialValue
342
                        ]
343
                    );
344 341
                } else if ($this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\TableGenerator')) {
345
                    throw MappingException::tableIdGeneratorNotImplemented($className);
346 341
                } else if ($customGeneratorAnnot = $this->reader->getPropertyAnnotation($property, Mapping\CustomIdGenerator::class)) {
347 2
                    $metadata->setCustomGeneratorDefinition(
348
                        [
349 344
                            'class' => $customGeneratorAnnot->class
350
                        ]
351
                    );
352
                }
353 251
            } else if ($oneToOneAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OneToOne::class)) {
354 108
                if ($idAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Id::class)) {
0 ignored issues
show
Unused Code introduced by
$idAnnot 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...
355 9
                    $mapping['id'] = true;
356
                }
357
358 108
                $mapping['targetEntity'] = $oneToOneAnnot->targetEntity;
359 108
                $mapping['joinColumns'] = $joinColumns;
360 108
                $mapping['mappedBy'] = $oneToOneAnnot->mappedBy;
361 108
                $mapping['inversedBy'] = $oneToOneAnnot->inversedBy;
362 108
                $mapping['cascade'] = $oneToOneAnnot->cascade;
363 108
                $mapping['orphanRemoval'] = $oneToOneAnnot->orphanRemoval;
364 108
                $mapping['fetch'] = $this->getFetchMode($className, $oneToOneAnnot->fetch);
365 108
                $metadata->mapOneToOne($mapping);
366 201
            } else if ($oneToManyAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OneToMany::class)) {
367 101
                $mapping['mappedBy'] = $oneToManyAnnot->mappedBy;
368 101
                $mapping['targetEntity'] = $oneToManyAnnot->targetEntity;
369 101
                $mapping['cascade'] = $oneToManyAnnot->cascade;
370 101
                $mapping['indexBy'] = $oneToManyAnnot->indexBy;
371 101
                $mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval;
372 101
                $mapping['fetch'] = $this->getFetchMode($className, $oneToManyAnnot->fetch);
373
374 101
                if ($orderByAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OrderBy::class)) {
375 14
                    $mapping['orderBy'] = $orderByAnnot->value;
376
                }
377
378 101
                $metadata->mapOneToMany($mapping);
379 198
            } else if ($manyToOneAnnot = $this->reader->getPropertyAnnotation($property, Mapping\ManyToOne::class)) {
380 128
                if ($idAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Id::class)) {
0 ignored issues
show
Unused Code introduced by
$idAnnot 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...
381 27
                    $mapping['id'] = true;
382
                }
383
384 128
                $mapping['joinColumns'] = $joinColumns;
385 128
                $mapping['cascade'] = $manyToOneAnnot->cascade;
386 128
                $mapping['inversedBy'] = $manyToOneAnnot->inversedBy;
387 128
                $mapping['targetEntity'] = $manyToOneAnnot->targetEntity;
388 128
                $mapping['fetch'] = $this->getFetchMode($className, $manyToOneAnnot->fetch);
389 128
                $metadata->mapManyToOne($mapping);
390 115
            } else if ($manyToManyAnnot = $this->reader->getPropertyAnnotation($property, Mapping\ManyToMany::class)) {
391 85
                $joinTable = [];
392
393 85
                if ($joinTableAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinTable::class)) {
394
                    $joinTable = [
395 65
                        'name' => $joinTableAnnot->name,
396 65
                        'schema' => $joinTableAnnot->schema
397
                    ];
398
399 65
                    foreach ($joinTableAnnot->joinColumns as $joinColumn) {
400 64
                        $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn);
401
                    }
402
403 65
                    foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) {
404 64
                        $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn);
405
                    }
406
                }
407
408 85
                $mapping['joinTable'] = $joinTable;
409 85
                $mapping['targetEntity'] = $manyToManyAnnot->targetEntity;
410 85
                $mapping['mappedBy'] = $manyToManyAnnot->mappedBy;
411 85
                $mapping['inversedBy'] = $manyToManyAnnot->inversedBy;
412 85
                $mapping['cascade'] = $manyToManyAnnot->cascade;
413 85
                $mapping['indexBy'] = $manyToManyAnnot->indexBy;
414 85
                $mapping['orphanRemoval'] = $manyToManyAnnot->orphanRemoval;
415 85
                $mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnot->fetch);
416
417 85
                if ($orderByAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OrderBy::class)) {
418 3
                    $mapping['orderBy'] = $orderByAnnot->value;
419
                }
420
421 85
                $metadata->mapManyToMany($mapping);
422 50
            } else if ($embeddedAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Embedded::class)) {
423 13
                $mapping['class'] = $embeddedAnnot->class;
424 13
                $mapping['columnPrefix'] = $embeddedAnnot->columnPrefix;
425
426 351
                $metadata->mapEmbedded($mapping);
427
            }
428
        }
429
430
        // Evaluate AssociationOverrides annotation
431 353
        if (isset($classAnnotations[Mapping\AssociationOverrides::class])) {
432 5
            $associationOverridesAnnot = $classAnnotations[Mapping\AssociationOverrides::class];
433
434 5
            foreach ($associationOverridesAnnot->value as $associationOverride) {
435 5
                $override   = [];
436 5
                $fieldName  = $associationOverride->name;
437
438
                // Check for JoinColumn/JoinColumns annotations
439 5
                if ($associationOverride->joinColumns) {
440 3
                    $joinColumns = [];
441
442 3
                    foreach ($associationOverride->joinColumns as $joinColumn) {
443 3
                        $joinColumns[] = $this->joinColumnToArray($joinColumn);
444
                    }
445
446 3
                    $override['joinColumns'] = $joinColumns;
447
                }
448
449
                // Check for JoinTable annotations
450 5
                if ($associationOverride->joinTable) {
451 2
                    $joinTableAnnot = $associationOverride->joinTable;
452
                    $joinTable      = [
453 2
                        'name'      => $joinTableAnnot->name,
454 2
                        'schema'    => $joinTableAnnot->schema
455
                    ];
456
457 2
                    foreach ($joinTableAnnot->joinColumns as $joinColumn) {
458 2
                        $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn);
459
                    }
460
461 2
                    foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) {
462 2
                        $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn);
463
                    }
464
465 2
                    $override['joinTable'] = $joinTable;
466
                }
467
468
                // Check for inversedBy
469 5
                if ($associationOverride->inversedBy) {
470 1
                    $override['inversedBy'] = $associationOverride->inversedBy;
471
                }
472
473
                // Check for `fetch`
474 5
                if ($associationOverride->fetch) {
475 1
                    $override['fetch'] = constant(Mapping\ClassMetadata::class . '::FETCH_' . $associationOverride->fetch);
476
                }
477
478 5
                $metadata->setAssociationOverride($fieldName, $override);
479
            }
480
        }
481
482
        // Evaluate AttributeOverrides annotation
483 353
        if (isset($classAnnotations[Mapping\AttributeOverrides::class])) {
484 3
            $attributeOverridesAnnot = $classAnnotations[Mapping\AttributeOverrides::class];
485
486 3
            foreach ($attributeOverridesAnnot->value as $attributeOverrideAnnot) {
487 3
                $attributeOverride = $this->columnToArray($attributeOverrideAnnot->name, $attributeOverrideAnnot->column);
488
489 3
                $metadata->setAttributeOverride($attributeOverrideAnnot->name, $attributeOverride);
490
            }
491
        }
492
493
        // Evaluate EntityListeners annotation
494 353
        if (isset($classAnnotations[Mapping\EntityListeners::class])) {
495 10
            $entityListenersAnnot = $classAnnotations[Mapping\EntityListeners::class];
496
497 10
            foreach ($entityListenersAnnot->value as $item) {
498 10
                $listenerClassName = $metadata->fullyQualifiedClassName($item);
499
500 10
                if ( ! class_exists($listenerClassName)) {
501
                    throw MappingException::entityListenerClassNotFound($listenerClassName, $className);
502
                }
503
504 10
                $hasMapping     = false;
505 10
                $listenerClass  = new \ReflectionClass($listenerClassName);
506
507
                /* @var $method \ReflectionMethod */
508 10
                foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
509
                    // find method callbacks.
510 10
                    $callbacks  = $this->getMethodCallbacks($method);
511 10
                    $hasMapping = $hasMapping ?: ( ! empty($callbacks));
512
513 10
                    foreach ($callbacks as $value) {
514 10
                        $metadata->addEntityListener($value[1], $listenerClassName, $value[0]);
515
                    }
516
                }
517
518
                // Evaluate the listener using naming convention.
519 10
                if ( ! $hasMapping ) {
520 10
                    EntityListenerBuilder::bindEntityListener($metadata, $listenerClassName);
0 ignored issues
show
Compatibility introduced by
$metadata of type object<Doctrine\ORM\Mapping\ClassMetadataInfo> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a child class of the class Doctrine\ORM\Mapping\ClassMetadataInfo to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
521
                }
522
            }
523
        }
524
525
        // Evaluate @HasLifecycleCallbacks annotation
526 353
        if (isset($classAnnotations[Mapping\HasLifecycleCallbacks::class])) {
527
            /* @var $method \ReflectionMethod */
528 17
            foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
529 16
                foreach ($this->getMethodCallbacks($method) as $value) {
530 16
                    $metadata->addLifecycleCallback($value[0], $value[1]);
531
                }
532
            }
533
        }
534 353
    }
535
536
    /**
537
     * Attempts to resolve the fetch mode.
538
     *
539
     * @param string $className The class name.
540
     * @param string $fetchMode The fetch mode.
541
     *
542
     * @return integer The fetch mode as defined in ClassMetadata.
543
     *
544
     * @throws MappingException If the fetch mode is not valid.
545
     */
546 237
    private function getFetchMode($className, $fetchMode)
547
    {
548 237
        if ( ! defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode)) {
549
            throw MappingException::invalidFetchMode($className, $fetchMode);
550
        }
551
552 237
        return constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode);
553
    }
554
555
    /**
556
     * Parses the given method.
557
     *
558
     * @param \ReflectionMethod $method
559
     *
560
     * @return array
561
     */
562 26
    private function getMethodCallbacks(\ReflectionMethod $method)
563
    {
564 26
        $callbacks   = [];
565 26
        $annotations = $this->reader->getMethodAnnotations($method);
566
567 26
        foreach ($annotations as $annot) {
568 20
            if ($annot instanceof Mapping\PrePersist) {
569 13
                $callbacks[] = [$method->name, Events::prePersist];
570
            }
571
572 20
            if ($annot instanceof Mapping\PostPersist) {
573 9
                $callbacks[] = [$method->name, Events::postPersist];
574
            }
575
576 20
            if ($annot instanceof Mapping\PreUpdate) {
577 11
                $callbacks[] = [$method->name, Events::preUpdate];
578
            }
579
580 20
            if ($annot instanceof Mapping\PostUpdate) {
581 7
                $callbacks[] = [$method->name, Events::postUpdate];
582
            }
583
584 20
            if ($annot instanceof Mapping\PreRemove) {
585 8
                $callbacks[] = [$method->name, Events::preRemove];
586
            }
587
588 20
            if ($annot instanceof Mapping\PostRemove) {
589 6
                $callbacks[] = [$method->name, Events::postRemove];
590
            }
591
592 20
            if ($annot instanceof Mapping\PostLoad) {
593 11
                $callbacks[] = [$method->name, Events::postLoad];
594
            }
595
596 20
            if ($annot instanceof Mapping\PreFlush) {
597 20
                $callbacks[] = [$method->name, Events::preFlush];
598
            }
599
        }
600
601 26
        return $callbacks;
602
    }
603
604
    /**
605
     * Parse the given JoinColumn as array
606
     *
607
     * @param Mapping\JoinColumn $joinColumn
608
     * @return array
609
     */
610 169
    private function joinColumnToArray(Mapping\JoinColumn $joinColumn)
611
    {
612
        return [
613 169
            'name' => $joinColumn->name,
614 169
            'unique' => $joinColumn->unique,
615 169
            'nullable' => $joinColumn->nullable,
616 169
            'onDelete' => $joinColumn->onDelete,
617 169
            'columnDefinition' => $joinColumn->columnDefinition,
618 169
            'referencedColumnName' => $joinColumn->referencedColumnName,
619
        ];
620
    }
621
622
    /**
623
     * Parse the given Column as array
624
     *
625
     * @param string $fieldName
626
     * @param Mapping\Column $column
627
     *
628
     * @return array
629
     */
630 344
    private function columnToArray($fieldName, Mapping\Column $column)
631
    {
632
        $mapping = [
633 344
            'fieldName' => $fieldName,
634 344
            'type'      => $column->type,
635 344
            'scale'     => $column->scale,
636 344
            'length'    => $column->length,
637 344
            'unique'    => $column->unique,
638 344
            'nullable'  => $column->nullable,
639 344
            'precision' => $column->precision
640
        ];
641
642 344
        if ($column->options) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $column->options 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...
643 6
            $mapping['options'] = $column->options;
644
        }
645
646 344
        if (isset($column->name)) {
647 75
            $mapping['columnName'] = $column->name;
648
        }
649
650 344
        if (isset($column->columnDefinition)) {
651 4
            $mapping['columnDefinition'] = $column->columnDefinition;
652
        }
653
654 344
        return $mapping;
655
    }
656
657
    /**
658
     * Factory method for the Annotation Driver.
659
     *
660
     * @param array|string          $paths
661
     * @param AnnotationReader|null $reader
662
     *
663
     * @return AnnotationDriver
664
     */
665
    static public function create($paths = [], AnnotationReader $reader = null)
666
    {
667
        if ($reader == null) {
668
            $reader = new AnnotationReader();
669
        }
670
671
        return new self($reader, $paths);
672
    }
673
}
674