Failed Conditions
Push — master ( da109c...fda770 )
by Marco
19:23
created

AnnotationDriver::getMethodCallbacks()   F

Complexity

Conditions 10
Paths 257

Size

Total Lines 41
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 10

Importance

Changes 0
Metric Value
dl 0
loc 41
c 0
b 0
f 0
ccs 21
cts 21
cp 1
rs 3.1304
cc 10
eloc 21
nc 257
nop 1
crap 10

How to fix   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 393
    public function loadMetadataForClass($className, ClassMetadata $metadata)
53
    {
54
        /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */
55 393
        $class = $metadata->getReflectionClass();
56
57 393
        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 393
        $classAnnotations = $this->reader->getClassAnnotations($class);
64
65 393
        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 390
            foreach ($classAnnotations as $key => $annot) {
67 390
                if ( ! is_numeric($key)) {
68
                    continue;
69
                }
70
71 390
                $classAnnotations[get_class($annot)] = $annot;
72
            }
73
        }
74
75
        // Evaluate Entity annotation
76 393
        if (isset($classAnnotations[Mapping\Entity::class])) {
77 385
            $entityAnnot = $classAnnotations[Mapping\Entity::class];
78 385
            if ($entityAnnot->repositoryClass !== null) {
79 8
                $metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
80
            }
81
82 385
            if ($entityAnnot->readOnly) {
83 385
                $metadata->markReadOnly();
84
            }
85 45
        } else if (isset($classAnnotations[Mapping\MappedSuperclass::class])) {
86 32
            $mappedSuperclassAnnot = $classAnnotations[Mapping\MappedSuperclass::class];
87
88 32
            $metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass);
89 32
            $metadata->isMappedSuperclass = true;
90 15
        } else if (isset($classAnnotations[Mapping\Embeddable::class])) {
91 12
            $metadata->isEmbeddedClass = true;
92
        } else {
93 3
            throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
94
        }
95
96
        // Evaluate Table annotation
97 390
        if (isset($classAnnotations[Mapping\Table::class])) {
98 204
            $tableAnnot   = $classAnnotations[Mapping\Table::class];
99
            $primaryTable = [
100 204
                'name'   => $tableAnnot->name,
101 204
                'schema' => $tableAnnot->schema
102
            ];
103
104 204
            if ($tableAnnot->indexes !== null) {
105 16
                foreach ($tableAnnot->indexes as $indexAnnot) {
106 16
                    $index = ['columns' => $indexAnnot->columns];
107
108 16
                    if ( ! empty($indexAnnot->flags)) {
109 1
                        $index['flags'] = $indexAnnot->flags;
110
                    }
111
112 16
                    if ( ! empty($indexAnnot->options)) {
113 1
                        $index['options'] = $indexAnnot->options;
114
                    }
115
116 16
                    if ( ! empty($indexAnnot->name)) {
117 15
                        $primaryTable['indexes'][$indexAnnot->name] = $index;
118
                    } else {
119 16
                        $primaryTable['indexes'][] = $index;
120
                    }
121
                }
122
            }
123
124 204
            if ($tableAnnot->uniqueConstraints !== null) {
125 9
                foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) {
126 9
                    $uniqueConstraint = ['columns' => $uniqueConstraintAnnot->columns];
127
128 9
                    if ( ! empty($uniqueConstraintAnnot->options)) {
129 3
                        $uniqueConstraint['options'] = $uniqueConstraintAnnot->options;
130
                    }
131
132 9
                    if ( ! empty($uniqueConstraintAnnot->name)) {
133 9
                        $primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint;
134
                    } else {
135 9
                        $primaryTable['uniqueConstraints'][] = $uniqueConstraint;
136
                    }
137
                }
138
            }
139
140 204
            if ($tableAnnot->options) {
141 6
                $primaryTable['options'] = $tableAnnot->options;
142
            }
143
144 204
            $metadata->setPrimaryTable($primaryTable);
145
        }
146
147
        // Evaluate @Cache annotation
148 390
        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 390
        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 390
        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 390
        if (isset($classAnnotations[Mapping\NamedQueries::class])) {
216 11
            $namedQueriesAnnot = $classAnnotations[Mapping\NamedQueries::class];
217
218 11
            if ( ! is_array($namedQueriesAnnot->value)) {
219
                throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations.");
220
            }
221
222 11
            foreach ($namedQueriesAnnot->value as $namedQuery) {
223 11
                if ( ! ($namedQuery instanceof Mapping\NamedQuery)) {
224
                    throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations.");
225
                }
226 11
                $metadata->addNamedQuery(
227
                    [
228 11
                        'name'  => $namedQuery->name,
229 11
                        'query' => $namedQuery->query
230
                    ]
231
                );
232
            }
233
        }
234
235
        // Evaluate InheritanceType annotation
236 390
        if (isset($classAnnotations[Mapping\InheritanceType::class])) {
237 75
            $inheritanceTypeAnnot = $classAnnotations[Mapping\InheritanceType::class];
238
239 75
            $metadata->setInheritanceType(
240 75
                constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAnnot->value)
241
            );
242
243 75
            if ($metadata->inheritanceType != Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) {
244
                // Evaluate DiscriminatorColumn annotation
245 75
                if (isset($classAnnotations[Mapping\DiscriminatorColumn::class])) {
246 57
                    $discrColumnAnnot = $classAnnotations[Mapping\DiscriminatorColumn::class];
247
248 57
                    $metadata->setDiscriminatorColumn(
249
                        [
250 57
                            'name'             => $discrColumnAnnot->name,
251 57
                            'type'             => $discrColumnAnnot->type ?: 'string',
252 57
                            'length'           => $discrColumnAnnot->length ?: 255,
253 57
                            'columnDefinition' => $discrColumnAnnot->columnDefinition,
254
                        ]
255
                    );
256
                } else {
257 21
                    $metadata->setDiscriminatorColumn(['name' => 'dtype', 'type' => 'string', 'length' => 255]);
258
                }
259
260
                // Evaluate DiscriminatorMap annotation
261 75
                if (isset($classAnnotations[Mapping\DiscriminatorMap::class])) {
262 74
                    $discrMapAnnot = $classAnnotations[Mapping\DiscriminatorMap::class];
263 74
                    $metadata->setDiscriminatorMap($discrMapAnnot->value);
264
                }
265
            }
266
        }
267
268
269
        // Evaluate DoctrineChangeTrackingPolicy annotation
270 390
        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 390
        foreach ($class->getProperties() as $property) {
278 388
            if ($metadata->isMappedSuperclass && ! $property->isPrivate()
279
                ||
280 388
                $metadata->isInheritedField($property->name)
281
                ||
282 388
                $metadata->isInheritedAssociation($property->name)
283
                ||
284 388
                $metadata->isInheritedEmbeddedClass($property->name)) {
285 78
                continue;
286
            }
287
288 388
            $mapping = [];
289 388
            $mapping['fieldName'] = $property->getName();
290
291
            // Evaluate @Cache annotation
292 388
            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 387
            $joinColumns = [];
303
304 387
            if ($joinColumnAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinColumn::class)) {
305 148
                $joinColumns[] = $this->joinColumnToArray($joinColumnAnnot);
306 386
            } else if ($joinColumnsAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinColumns::class)) {
307 19
                foreach ($joinColumnsAnnot->value as $joinColumn) {
308 19
                    $joinColumns[] = $this->joinColumnToArray($joinColumn);
309
                }
310
            }
311
312
            // Field can only be annotated with one of:
313
            // @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany
0 ignored issues
show
Unused Code Comprehensibility introduced by
48% 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...
314 387
            if ($columnAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Column::class)) {
315 379
                if ($columnAnnot->type == null) {
316
                    throw MappingException::propertyTypeIsRequired($className, $property->getName());
317
                }
318
319 379
                $mapping = $this->columnToArray($property->getName(), $columnAnnot);
320
321 379
                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 372
                    $mapping['id'] = true;
323
                }
324
325 379
                if ($generatedValueAnnot = $this->reader->getPropertyAnnotation($property, Mapping\GeneratedValue::class)) {
326 318
                    $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $generatedValueAnnot->strategy));
327
                }
328
329 379
                if ($this->reader->getPropertyAnnotation($property, Mapping\Version::class)) {
330 16
                    $metadata->setVersionMapping($mapping);
331
                }
332
333 379
                $metadata->mapField($mapping);
334
335
                // Check for SequenceGenerator/TableGenerator definition
336 379
                if ($seqGeneratorAnnot = $this->reader->getPropertyAnnotation($property, Mapping\SequenceGenerator::class)) {
337 9
                    $metadata->setSequenceGeneratorDefinition(
338
                        [
339 9
                            'sequenceName' => $seqGeneratorAnnot->sequenceName,
340 9
                            'allocationSize' => $seqGeneratorAnnot->allocationSize,
341 9
                            'initialValue' => $seqGeneratorAnnot->initialValue
342
                        ]
343
                    );
344 376
                } else if ($this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\TableGenerator')) {
345
                    throw MappingException::tableIdGeneratorNotImplemented($className);
346 376
                } else if ($customGeneratorAnnot = $this->reader->getPropertyAnnotation($property, Mapping\CustomIdGenerator::class)) {
347 3
                    $metadata->setCustomGeneratorDefinition(
348
                        [
349 379
                            'class' => $customGeneratorAnnot->class
350
                        ]
351
                    );
352
                }
353 273
            } else if ($oneToOneAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OneToOne::class)) {
354 118
                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 118
                $mapping['targetEntity'] = $oneToOneAnnot->targetEntity;
359 118
                $mapping['joinColumns'] = $joinColumns;
360 118
                $mapping['mappedBy'] = $oneToOneAnnot->mappedBy;
361 118
                $mapping['inversedBy'] = $oneToOneAnnot->inversedBy;
362 118
                $mapping['cascade'] = $oneToOneAnnot->cascade;
363 118
                $mapping['orphanRemoval'] = $oneToOneAnnot->orphanRemoval;
364 118
                $mapping['fetch'] = $this->getFetchMode($className, $oneToOneAnnot->fetch);
365 118
                $metadata->mapOneToOne($mapping);
366 222
            } else if ($oneToManyAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OneToMany::class)) {
367 111
                $mapping['mappedBy'] = $oneToManyAnnot->mappedBy;
368 111
                $mapping['targetEntity'] = $oneToManyAnnot->targetEntity;
369 111
                $mapping['cascade'] = $oneToManyAnnot->cascade;
370 111
                $mapping['indexBy'] = $oneToManyAnnot->indexBy;
371 111
                $mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval;
372 111
                $mapping['fetch'] = $this->getFetchMode($className, $oneToManyAnnot->fetch);
373
374 111
                if ($orderByAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OrderBy::class)) {
375 16
                    $mapping['orderBy'] = $orderByAnnot->value;
376
                }
377
378 111
                $metadata->mapOneToMany($mapping);
379 219
            } else if ($manyToOneAnnot = $this->reader->getPropertyAnnotation($property, Mapping\ManyToOne::class)) {
380 139
                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 31
                    $mapping['id'] = true;
382
                }
383
384 139
                $mapping['joinColumns'] = $joinColumns;
385 139
                $mapping['cascade'] = $manyToOneAnnot->cascade;
386 139
                $mapping['inversedBy'] = $manyToOneAnnot->inversedBy;
387 139
                $mapping['targetEntity'] = $manyToOneAnnot->targetEntity;
388 139
                $mapping['fetch'] = $this->getFetchMode($className, $manyToOneAnnot->fetch);
389 139
                $metadata->mapManyToOne($mapping);
390 126
            } else if ($manyToManyAnnot = $this->reader->getPropertyAnnotation($property, Mapping\ManyToMany::class)) {
391 95
                $joinTable = [];
392
393 95
                if ($joinTableAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinTable::class)) {
394
                    $joinTable = [
395 75
                        'name' => $joinTableAnnot->name,
396 75
                        'schema' => $joinTableAnnot->schema
397
                    ];
398
399 75
                    foreach ($joinTableAnnot->joinColumns as $joinColumn) {
400 74
                        $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn);
401
                    }
402
403 75
                    foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) {
404 74
                        $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn);
405
                    }
406
                }
407
408 95
                $mapping['joinTable'] = $joinTable;
409 95
                $mapping['targetEntity'] = $manyToManyAnnot->targetEntity;
410 95
                $mapping['mappedBy'] = $manyToManyAnnot->mappedBy;
411 95
                $mapping['inversedBy'] = $manyToManyAnnot->inversedBy;
412 95
                $mapping['cascade'] = $manyToManyAnnot->cascade;
413 95
                $mapping['indexBy'] = $manyToManyAnnot->indexBy;
414 95
                $mapping['orphanRemoval'] = $manyToManyAnnot->orphanRemoval;
415 95
                $mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnot->fetch);
416
417 95
                if ($orderByAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OrderBy::class)) {
418 3
                    $mapping['orderBy'] = $orderByAnnot->value;
419
                }
420
421 95
                $metadata->mapManyToMany($mapping);
422 51
            } else if ($embeddedAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Embedded::class)) {
423 13
                $mapping['class'] = $embeddedAnnot->class;
424 13
                $mapping['columnPrefix'] = $embeddedAnnot->columnPrefix;
425
426 386
                $metadata->mapEmbedded($mapping);
427
            }
428
        }
429
430
        // Evaluate AssociationOverrides annotation
431 388
        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`
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
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 388
        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 388
        if (isset($classAnnotations[Mapping\EntityListeners::class])) {
495 12
            $entityListenersAnnot = $classAnnotations[Mapping\EntityListeners::class];
496
497 12
            foreach ($entityListenersAnnot->value as $item) {
498 12
                $listenerClassName = $metadata->fullyQualifiedClassName($item);
499
500 12
                if ( ! class_exists($listenerClassName)) {
501
                    throw MappingException::entityListenerClassNotFound($listenerClassName, $className);
502
                }
503
504 12
                $hasMapping     = false;
505 12
                $listenerClass  = new \ReflectionClass($listenerClassName);
506
507
                /* @var $method \ReflectionMethod */
508 12
                foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
509
                    // find method callbacks.
510 12
                    $callbacks  = $this->getMethodCallbacks($method);
511 12
                    $hasMapping = $hasMapping ?: ( ! empty($callbacks));
512
513 12
                    foreach ($callbacks as $value) {
514 12
                        $metadata->addEntityListener($value[1], $listenerClassName, $value[0]);
515
                    }
516
                }
517
518
                // Evaluate the listener using naming convention.
519 12
                if ( ! $hasMapping ) {
520 12
                    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 388
        if (isset($classAnnotations[Mapping\HasLifecycleCallbacks::class])) {
527
            /* @var $method \ReflectionMethod */
528 19
            foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
529 18
                foreach ($this->getMethodCallbacks($method) as $value) {
530 18
                    $metadata->addLifecycleCallback($value[0], $value[1]);
531
                }
532
            }
533
        }
534 388
    }
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 259
    private function getFetchMode($className, $fetchMode)
547
    {
548 259
        if ( ! defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode)) {
549
            throw MappingException::invalidFetchMode($className, $fetchMode);
550
        }
551
552 259
        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 28
    private function getMethodCallbacks(\ReflectionMethod $method)
563
    {
564 28
        $callbacks   = [];
565 28
        $annotations = $this->reader->getMethodAnnotations($method);
566
567 28
        foreach ($annotations as $annot) {
568 22
            if ($annot instanceof Mapping\PrePersist) {
569 15
                $callbacks[] = [$method->name, Events::prePersist];
570
            }
571
572 22
            if ($annot instanceof Mapping\PostPersist) {
573 11
                $callbacks[] = [$method->name, Events::postPersist];
574
            }
575
576 22
            if ($annot instanceof Mapping\PreUpdate) {
577 11
                $callbacks[] = [$method->name, Events::preUpdate];
578
            }
579
580 22
            if ($annot instanceof Mapping\PostUpdate) {
581 7
                $callbacks[] = [$method->name, Events::postUpdate];
582
            }
583
584 22
            if ($annot instanceof Mapping\PreRemove) {
585 8
                $callbacks[] = [$method->name, Events::preRemove];
586
            }
587
588 22
            if ($annot instanceof Mapping\PostRemove) {
589 6
                $callbacks[] = [$method->name, Events::postRemove];
590
            }
591
592 22
            if ($annot instanceof Mapping\PostLoad) {
593 11
                $callbacks[] = [$method->name, Events::postLoad];
594
            }
595
596 22
            if ($annot instanceof Mapping\PreFlush) {
597 22
                $callbacks[] = [$method->name, Events::preFlush];
598
            }
599
        }
600
601 28
        return $callbacks;
602
    }
603
604
    /**
605
     * Parse the given JoinColumn as array
606
     *
607
     * @param Mapping\JoinColumn $joinColumn
608
     * @return array
609
     */
610 186
    private function joinColumnToArray(Mapping\JoinColumn $joinColumn)
611
    {
612
        return [
613 186
            'name' => $joinColumn->name,
614 186
            'unique' => $joinColumn->unique,
615 186
            'nullable' => $joinColumn->nullable,
616 186
            'onDelete' => $joinColumn->onDelete,
617 186
            'columnDefinition' => $joinColumn->columnDefinition,
618 186
            '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 379
    private function columnToArray($fieldName, Mapping\Column $column)
631
    {
632
        $mapping = [
633 379
            'fieldName' => $fieldName,
634 379
            'type'      => $column->type,
635 379
            'scale'     => $column->scale,
636 379
            'length'    => $column->length,
637 379
            'unique'    => $column->unique,
638 379
            'nullable'  => $column->nullable,
639 379
            'precision' => $column->precision
640
        ];
641
642 379
        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 8
            $mapping['options'] = $column->options;
644
        }
645
646 379
        if (isset($column->name)) {
647 84
            $mapping['columnName'] = $column->name;
648
        }
649
650 379
        if (isset($column->columnDefinition)) {
651 6
            $mapping['columnDefinition'] = $column->columnDefinition;
652
        }
653
654 379
        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)
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
666
    {
667
        if ($reader == null) {
668
            $reader = new AnnotationReader();
669
        }
670
671
        return new self($reader, $paths);
672
    }
673
}
674