Completed
Pull Request — master (#5800)
by Herberto
10:37
created

AnnotationDriver   D

Complexity

Total Complexity 110

Size/Duplication

Total Lines 619
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 96.38%

Importance

Changes 5
Bugs 3 Features 0
Metric Value
wmc 110
c 5
b 3
f 0
lcom 1
cbo 8
dl 0
loc 619
ccs 293
cts 304
cp 0.9638
rs 4.7957

6 Methods

Rating   Name   Duplication   Size   Complexity  
F loadMetadataForClass() 0 467 91
A getFetchMode() 0 8 2
F getMethodCallbacks() 0 41 10
A joinColumnToArray() 0 11 1
B columnToArray() 0 26 4
A create() 0 8 2

How to fix   Complexity   

Complex Class

Complex classes like AnnotationDriver often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AnnotationDriver, and based on these observations, apply Extract Interface, too.

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\ORM\Mapping\MappingException;
24
use Doctrine\ORM\Mapping\JoinColumn;
25
use Doctrine\ORM\Mapping\Column;
26
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
27
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
28
use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver;
29
use Doctrine\ORM\Events;
30
31
/**
32
 * The AnnotationDriver reads the mapping metadata from docblock annotations.
33
 *
34
 * @since 2.0
35
 * @author Benjamin Eberlei <[email protected]>
36
 * @author Guilherme Blanco <[email protected]>
37
 * @author Jonathan H. Wage <[email protected]>
38
 * @author Roman Borschel <[email protected]>
39
 */
40
class AnnotationDriver extends AbstractAnnotationDriver
41
{
42
    /**
43
     * {@inheritDoc}
44
     */
45
    protected $entityAnnotationClasses = array(
46
        'Doctrine\ORM\Mapping\Entity' => 1,
47
        'Doctrine\ORM\Mapping\MappedSuperclass' => 2,
48
    );
49
50
    /**
51
     * {@inheritDoc}
52
     */
53 354
    public function loadMetadataForClass($className, ClassMetadata $metadata)
54
    {
55
        /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */
56 354
        $class = $metadata->getReflectionClass();
57
58 354
        if ( ! $class) {
59
            // this happens when running annotation driver in combination with
60
            // static reflection services. This is not the nicest fix
61 1
            $class = new \ReflectionClass($metadata->name);
62
        }
63
64 354
        $classAnnotations = $this->reader->getClassAnnotations($class);
65
66 354
        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...
67 351
            foreach ($classAnnotations as $key => $annot) {
68 351
                if ( ! is_numeric($key)) {
69
                    continue;
70
                }
71
72 351
                $classAnnotations[get_class($annot)] = $annot;
73
            }
74
        }
75
76
        // Evaluate Entity annotation
77 354
        if (isset($classAnnotations['Doctrine\ORM\Mapping\Entity'])) {
78 346
            $entityAnnot = $classAnnotations['Doctrine\ORM\Mapping\Entity'];
79 346
            if ($entityAnnot->repositoryClass !== null) {
80 8
                $metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
81
            }
82
            
83 346
            if ($entityAnnot->collectionClass !== null) {
84 4
                $metadata->setCustomCollectionClass($entityAnnot->collectionClass);
85
            }
86
            
87 346
            if ($entityAnnot->readOnly) {
88 346
                $metadata->markReadOnly();
89
            }
90 43
        } else if (isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'])) {
91 31
            $mappedSuperclassAnnot = $classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'];
92
93 31
            $metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass);
94 31
            $metadata->isMappedSuperclass = true;
95 13
        } else if (isset($classAnnotations['Doctrine\ORM\Mapping\Embeddable'])) {
96 10
            $metadata->isEmbeddedClass = true;
97
        } else {
98 3
            throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
99
        }
100
101
        // Evaluate Table annotation
102 351
        if (isset($classAnnotations['Doctrine\ORM\Mapping\Table'])) {
103 190
            $tableAnnot   = $classAnnotations['Doctrine\ORM\Mapping\Table'];
104
            $primaryTable = array(
105 190
                'name'   => $tableAnnot->name,
106 190
                'schema' => $tableAnnot->schema
107
            );
108
109 190
            if ($tableAnnot->indexes !== null) {
110 13
                foreach ($tableAnnot->indexes as $indexAnnot) {
111 13
                    $index = array('columns' => $indexAnnot->columns);
112
113 13
                    if ( ! empty($indexAnnot->flags)) {
114 1
                        $index['flags'] = $indexAnnot->flags;
115
                    }
116
117 13
                    if ( ! empty($indexAnnot->options)) {
118 1
                        $index['options'] = $indexAnnot->options;
119
                    }
120
121 13
                    if ( ! empty($indexAnnot->name)) {
122 12
                        $primaryTable['indexes'][$indexAnnot->name] = $index;
123
                    } else {
124 13
                        $primaryTable['indexes'][] = $index;
125
                    }
126
                }
127
            }
128
129 190
            if ($tableAnnot->uniqueConstraints !== null) {
130 8
                foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) {
131 8
                    $uniqueConstraint = array('columns' => $uniqueConstraintAnnot->columns);
132
133 8
                    if ( ! empty($uniqueConstraintAnnot->options)) {
134 2
                        $uniqueConstraint['options'] = $uniqueConstraintAnnot->options;
135
                    }
136
137 8
                    if ( ! empty($uniqueConstraintAnnot->name)) {
138 8
                        $primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint;
139
                    } else {
140 8
                        $primaryTable['uniqueConstraints'][] = $uniqueConstraint;
141
                    }
142
                }
143
            }
144
145 190
            if ($tableAnnot->options) {
146 5
                $primaryTable['options'] = $tableAnnot->options;
147
            }
148
149 190
            $metadata->setPrimaryTable($primaryTable);
150
        }
151
152
        // Evaluate @Cache annotation
153 351
        if (isset($classAnnotations['Doctrine\ORM\Mapping\Cache'])) {
154 14
            $cacheAnnot = $classAnnotations['Doctrine\ORM\Mapping\Cache'];
155
            $cacheMap   = array(
156 14
                'region' => $cacheAnnot->region,
157 14
                'usage'  => constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage),
158
            );
159
160 14
            $metadata->enableCache($cacheMap);
161
        }
162
163
        // Evaluate NamedNativeQueries annotation
164 351
        if (isset($classAnnotations['Doctrine\ORM\Mapping\NamedNativeQueries'])) {
165 14
            $namedNativeQueriesAnnot = $classAnnotations['Doctrine\ORM\Mapping\NamedNativeQueries'];
166
167 14
            foreach ($namedNativeQueriesAnnot->value as $namedNativeQuery) {
168 14
                $metadata->addNamedNativeQuery(array(
169 14
                    'name'              => $namedNativeQuery->name,
170 14
                    'query'             => $namedNativeQuery->query,
171 14
                    'resultClass'       => $namedNativeQuery->resultClass,
172 14
                    'resultSetMapping'  => $namedNativeQuery->resultSetMapping,
173
                ));
174
            }
175
        }
176
177
        // Evaluate SqlResultSetMappings annotation
178 351
        if (isset($classAnnotations['Doctrine\ORM\Mapping\SqlResultSetMappings'])) {
179 14
            $sqlResultSetMappingsAnnot = $classAnnotations['Doctrine\ORM\Mapping\SqlResultSetMappings'];
180
181 14
            foreach ($sqlResultSetMappingsAnnot->value as $resultSetMapping) {
182 14
                $entities = array();
183 14
                $columns  = array();
184 14
                foreach ($resultSetMapping->entities as $entityResultAnnot) {
185
                    $entityResult = array(
186 14
                        'fields'                => array(),
187 14
                        'entityClass'           => $entityResultAnnot->entityClass,
188 14
                        'discriminatorColumn'   => $entityResultAnnot->discriminatorColumn,
189
                    );
190
191 14
                    foreach ($entityResultAnnot->fields as $fieldResultAnnot) {
192 14
                        $entityResult['fields'][] = array(
193 14
                            'name'      => $fieldResultAnnot->name,
194 14
                            'column'    => $fieldResultAnnot->column
195
                        );
196
                    }
197
198 14
                    $entities[] = $entityResult;
199
                }
200
201 14
                foreach ($resultSetMapping->columns as $columnResultAnnot) {
202 9
                    $columns[] = array(
203 9
                        'name' => $columnResultAnnot->name,
204
                    );
205
                }
206
207 14
                $metadata->addSqlResultSetMapping(array(
208 14
                    'name'          => $resultSetMapping->name,
209 14
                    'entities'      => $entities,
210 14
                    'columns'       => $columns
211
                ));
212
            }
213
        }
214
215
        // Evaluate NamedQueries annotation
216 351
        if (isset($classAnnotations['Doctrine\ORM\Mapping\NamedQueries'])) {
217 9
            $namedQueriesAnnot = $classAnnotations['Doctrine\ORM\Mapping\NamedQueries'];
218
219 9
            if ( ! is_array($namedQueriesAnnot->value)) {
220
                throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations.");
221
            }
222
223 9
            foreach ($namedQueriesAnnot->value as $namedQuery) {
224 9
                if ( ! ($namedQuery instanceof \Doctrine\ORM\Mapping\NamedQuery)) {
225
                    throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations.");
226
                }
227 9
                $metadata->addNamedQuery(array(
228 9
                    'name'  => $namedQuery->name,
229 9
                    'query' => $namedQuery->query
230
                ));
231
            }
232
        }
233
234
        // Evaluate InheritanceType annotation
235 351
        if (isset($classAnnotations['Doctrine\ORM\Mapping\InheritanceType'])) {
236 65
            $inheritanceTypeAnnot = $classAnnotations['Doctrine\ORM\Mapping\InheritanceType'];
237
238 65
            $metadata->setInheritanceType(
239 65
                constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAnnot->value)
240
            );
241
242 65
            if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) {
243
                // Evaluate DiscriminatorColumn annotation
244 65
                if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorColumn'])) {
245 48
                    $discrColumnAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorColumn'];
246
247 48
                    $metadata->setDiscriminatorColumn(array(
248 48
                        'name'             => $discrColumnAnnot->name,
249 48
                        'type'             => $discrColumnAnnot->type ?: 'string',
250 48
                        'length'           => $discrColumnAnnot->length ?: 255,
251 48
                        'columnDefinition' => $discrColumnAnnot->columnDefinition,
252
                    ));
253
                } else {
254 20
                    $metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255));
255
                }
256
257
                // Evaluate DiscriminatorMap annotation
258 65
                if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap'])) {
259 64
                    $discrMapAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap'];
260 64
                    $metadata->setDiscriminatorMap($discrMapAnnot->value);
261
                }
262
            }
263
        }
264
265
266
        // Evaluate DoctrineChangeTrackingPolicy annotation
267 351
        if (isset($classAnnotations['Doctrine\ORM\Mapping\ChangeTrackingPolicy'])) {
268 5
            $changeTrackingAnnot = $classAnnotations['Doctrine\ORM\Mapping\ChangeTrackingPolicy'];
269 5
            $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' . $changeTrackingAnnot->value));
270
        }
271
272
        // Evaluate annotations on properties/fields
273
        /* @var $property \ReflectionProperty */
274 351
        foreach ($class->getProperties() as $property) {
275 349
            if ($metadata->isMappedSuperclass && ! $property->isPrivate()
276
                ||
277 349
                $metadata->isInheritedField($property->name)
278
                ||
279 349
                $metadata->isInheritedAssociation($property->name)
280
                ||
281 349
                $metadata->isInheritedEmbeddedClass($property->name)) {
282 69
                continue;
283
            }
284
285 349
            $mapping = array();
286 349
            $mapping['fieldName'] = $property->getName();
287
288
            // Evaluate @Cache annotation
289 349
            if (($cacheAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Cache')) !== null) {
290 12
                $mapping['cache'] = $metadata->getAssociationCacheDefaults($mapping['fieldName'], array(
291 12
                        'usage'         => constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage),
292 12
                        'region'        => $cacheAnnot->region,
293
                ));
294
            }
295
            // Check for JoinColumn/JoinColumns annotations
296 348
            $joinColumns = array();
297
298 348
            if ($joinColumnAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumn')) {
299 135
                $joinColumns[] = $this->joinColumnToArray($joinColumnAnnot);
300 347
            } else if ($joinColumnsAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumns')) {
301 18
                foreach ($joinColumnsAnnot->value as $joinColumn) {
302 18
                    $joinColumns[] = $this->joinColumnToArray($joinColumn);
303
                }
304
            }
305
306
            // Field can only be annotated with one of:
307
            // @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany
308 348
            if ($columnAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Column')) {
309 340
                if ($columnAnnot->type == null) {
310
                    throw MappingException::propertyTypeIsRequired($className, $property->getName());
311
                }
312
313 340
                $mapping = $this->columnToArray($property->getName(), $columnAnnot);
314
315 340
                if ($idAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) {
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...
316 333
                    $mapping['id'] = true;
317
                }
318
319 340
                if ($generatedValueAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\GeneratedValue')) {
320 293
                    $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $generatedValueAnnot->strategy));
321
                }
322
323 340
                if ($this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Version')) {
324 13
                    $metadata->setVersionMapping($mapping);
325
                }
326
327 340
                $metadata->mapField($mapping);
328
329
                // Check for SequenceGenerator/TableGenerator definition
330 340
                if ($seqGeneratorAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\SequenceGenerator')) {
331 8
                    $metadata->setSequenceGeneratorDefinition(array(
332 8
                        'sequenceName' => $seqGeneratorAnnot->sequenceName,
333 8
                        'allocationSize' => $seqGeneratorAnnot->allocationSize,
334 8
                        'initialValue' => $seqGeneratorAnnot->initialValue
335
                    ));
336 337
                } else if ($this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\TableGenerator')) {
337
                    throw MappingException::tableIdGeneratorNotImplemented($className);
338 337
                } else if ($customGeneratorAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\CustomIdGenerator')) {
339 2
                    $metadata->setCustomGeneratorDefinition(array(
340 340
                        'class' => $customGeneratorAnnot->class
341
                    ));
342
                }
343 257
            } else if ($oneToOneAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToOne')) {
344 106
                if ($idAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) {
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...
345 9
                    $mapping['id'] = true;
346
                }
347
348 106
                $mapping['targetEntity'] = $oneToOneAnnot->targetEntity;
349 106
                $mapping['joinColumns'] = $joinColumns;
350 106
                $mapping['mappedBy'] = $oneToOneAnnot->mappedBy;
351 106
                $mapping['inversedBy'] = $oneToOneAnnot->inversedBy;
352 106
                $mapping['cascade'] = $oneToOneAnnot->cascade;
353 106
                $mapping['orphanRemoval'] = $oneToOneAnnot->orphanRemoval;
354 106
                $mapping['fetch'] = $this->getFetchMode($className, $oneToOneAnnot->fetch);
355 106
                $metadata->mapOneToOne($mapping);
356 208
            } else if ($oneToManyAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToMany')) {
357 103
                $mapping['mappedBy'] = $oneToManyAnnot->mappedBy;
358 103
                $mapping['targetEntity'] = $oneToManyAnnot->targetEntity;
359 103
                $mapping['cascade'] = $oneToManyAnnot->cascade;
360 103
                $mapping['indexBy'] = $oneToManyAnnot->indexBy;
361 103
                $mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval;
362 103
                $mapping['fetch'] = $this->getFetchMode($className, $oneToManyAnnot->fetch);
363
364 103
                if ($orderByAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
365 15
                    $mapping['orderBy'] = $orderByAnnot->value;
366
                }
367
368 103
                $metadata->mapOneToMany($mapping);
369 205
            } else if ($manyToOneAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToOne')) {
370 135
                if ($idAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) {
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...
371 30
                    $mapping['id'] = true;
372
                }
373
374 135
                $mapping['joinColumns'] = $joinColumns;
375 135
                $mapping['cascade'] = $manyToOneAnnot->cascade;
376 135
                $mapping['inversedBy'] = $manyToOneAnnot->inversedBy;
377 135
                $mapping['targetEntity'] = $manyToOneAnnot->targetEntity;
378 135
                $mapping['fetch'] = $this->getFetchMode($className, $manyToOneAnnot->fetch);
379 135
                $metadata->mapManyToOne($mapping);
380 114
            } else if ($manyToManyAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToMany')) {
381 86
                $joinTable = array();
382
383 86
                if ($joinTableAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinTable')) {
384
                    $joinTable = array(
385 67
                        'name' => $joinTableAnnot->name,
386 67
                        'schema' => $joinTableAnnot->schema
387
                    );
388
389 67
                    foreach ($joinTableAnnot->joinColumns as $joinColumn) {
390 65
                        $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn);
391
                    }
392
393 67
                    foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) {
394 65
                        $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn);
395
                    }
396
                }
397
398 86
                $mapping['joinTable'] = $joinTable;
399 86
                $mapping['targetEntity'] = $manyToManyAnnot->targetEntity;
400 86
                $mapping['mappedBy'] = $manyToManyAnnot->mappedBy;
401 86
                $mapping['inversedBy'] = $manyToManyAnnot->inversedBy;
402 86
                $mapping['cascade'] = $manyToManyAnnot->cascade;
403 86
                $mapping['indexBy'] = $manyToManyAnnot->indexBy;
404 86
                $mapping['orphanRemoval'] = $manyToManyAnnot->orphanRemoval;
405 86
                $mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnot->fetch);
406
407 86
                if ($orderByAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
408 3
                    $mapping['orderBy'] = $orderByAnnot->value;
409
                }
410
411 86
                $metadata->mapManyToMany($mapping);
412 45
            } else if ($embeddedAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Embedded')) {
413 11
                $mapping['class'] = $embeddedAnnot->class;
414 11
                $mapping['columnPrefix'] = $embeddedAnnot->columnPrefix;
415
416 347
                $metadata->mapEmbedded($mapping);
417
            }
418
        }
419
420
        // Evaluate AssociationOverrides annotation
421 349
        if (isset($classAnnotations['Doctrine\ORM\Mapping\AssociationOverrides'])) {
422 4
            $associationOverridesAnnot = $classAnnotations['Doctrine\ORM\Mapping\AssociationOverrides'];
423
424 4
            foreach ($associationOverridesAnnot->value as $associationOverride) {
425 4
                $override   = array();
426 4
                $fieldName  = $associationOverride->name;
427
428
                // Check for JoinColumn/JoinColumns annotations
429 4
                if ($associationOverride->joinColumns) {
430 3
                    $joinColumns = array();
431
432 3
                    foreach ($associationOverride->joinColumns as $joinColumn) {
433 3
                        $joinColumns[] = $this->joinColumnToArray($joinColumn);
434
                    }
435
436 3
                    $override['joinColumns'] = $joinColumns;
437
                }
438
439
                // Check for JoinTable annotations
440 4
                if ($associationOverride->joinTable) {
441 2
                    $joinTableAnnot = $associationOverride->joinTable;
442
                    $joinTable      = array(
443 2
                        'name'      => $joinTableAnnot->name,
444 2
                        'schema'    => $joinTableAnnot->schema
445
                    );
446
447 2
                    foreach ($joinTableAnnot->joinColumns as $joinColumn) {
448 2
                        $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn);
449
                    }
450
451 2
                    foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) {
452 2
                        $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn);
453
                    }
454
455 2
                    $override['joinTable'] = $joinTable;
456
                }
457
458
                // Check for inversedBy
459 4
                if ($associationOverride->inversedBy) {
460 1
                    $override['inversedBy'] = $associationOverride->inversedBy;
461
                }
462
463 4
                $metadata->setAssociationOverride($fieldName, $override);
464
            }
465
        }
466
467
        // Evaluate AttributeOverrides annotation
468 349
        if (isset($classAnnotations['Doctrine\ORM\Mapping\AttributeOverrides'])) {
469 3
            $attributeOverridesAnnot = $classAnnotations['Doctrine\ORM\Mapping\AttributeOverrides'];
470
471 3
            foreach ($attributeOverridesAnnot->value as $attributeOverrideAnnot) {
472 3
                $attributeOverride = $this->columnToArray($attributeOverrideAnnot->name, $attributeOverrideAnnot->column);
473
474 3
                $metadata->setAttributeOverride($attributeOverrideAnnot->name, $attributeOverride);
475
            }
476
        }
477
478
        // Evaluate EntityListeners annotation
479 349
        if (isset($classAnnotations['Doctrine\ORM\Mapping\EntityListeners'])) {
480 10
            $entityListenersAnnot = $classAnnotations['Doctrine\ORM\Mapping\EntityListeners'];
481
482 10
            foreach ($entityListenersAnnot->value as $item) {
483 10
                $listenerClassName = $metadata->fullyQualifiedClassName($item);
484
485 10
                if ( ! class_exists($listenerClassName)) {
486
                    throw MappingException::entityListenerClassNotFound($listenerClassName, $className);
487
                }
488
489 10
                $hasMapping     = false;
490 10
                $listenerClass  = new \ReflectionClass($listenerClassName);
491
492
                /* @var $method \ReflectionMethod */
493 10
                foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
494
                    // find method callbacks.
495 10
                    $callbacks  = $this->getMethodCallbacks($method);
496 10
                    $hasMapping = $hasMapping ?: ( ! empty($callbacks));
497
498 10
                    foreach ($callbacks as $value) {
499 10
                        $metadata->addEntityListener($value[1], $listenerClassName, $value[0]);
500
                    }
501
                }
502
503
                // Evaluate the listener using naming convention.
504 10
                if ( ! $hasMapping ) {
505 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...
506
                }
507
            }
508
        }
509
510
        // Evaluate @HasLifecycleCallbacks annotation
511 349
        if (isset($classAnnotations['Doctrine\ORM\Mapping\HasLifecycleCallbacks'])) {
512
            /* @var $method \ReflectionMethod */
513 18
            foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
514 17
                foreach ($this->getMethodCallbacks($method) as $value) {
515 17
                    $metadata->addLifecycleCallback($value[0], $value[1]);
516
                }
517
            }
518
        }
519 349
    }
520
521
    /**
522
     * Attempts to resolve the fetch mode.
523
     *
524
     * @param string $className The class name.
525
     * @param string $fetchMode The fetch mode.
526
     *
527
     * @return integer The fetch mode as defined in ClassMetadata.
528
     *
529
     * @throws MappingException If the fetch mode is not valid.
530
     */
531 244
    private function getFetchMode($className, $fetchMode)
532
    {
533 244
        if ( ! defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode)) {
534
            throw MappingException::invalidFetchMode($className, $fetchMode);
535
        }
536
537 244
        return constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode);
538
    }
539
540
    /**
541
     * Parses the given method.
542
     *
543
     * @param \ReflectionMethod $method
544
     *
545
     * @return array
546
     */
547 27
    private function getMethodCallbacks(\ReflectionMethod $method)
548
    {
549 27
        $callbacks   = array();
550 27
        $annotations = $this->reader->getMethodAnnotations($method);
551
552 27
        foreach ($annotations as $annot) {
553 21
            if ($annot instanceof \Doctrine\ORM\Mapping\PrePersist) {
554 14
                $callbacks[] = array($method->name, Events::prePersist);
555
            }
556
557 21
            if ($annot instanceof \Doctrine\ORM\Mapping\PostPersist) {
558 10
                $callbacks[] = array($method->name, Events::postPersist);
559
            }
560
561 21
            if ($annot instanceof \Doctrine\ORM\Mapping\PreUpdate) {
562 11
                $callbacks[] = array($method->name, Events::preUpdate);
563
            }
564
565 21
            if ($annot instanceof \Doctrine\ORM\Mapping\PostUpdate) {
566 7
                $callbacks[] = array($method->name, Events::postUpdate);
567
            }
568
569 21
            if ($annot instanceof \Doctrine\ORM\Mapping\PreRemove) {
570 8
                $callbacks[] = array($method->name, Events::preRemove);
571
            }
572
573 21
            if ($annot instanceof \Doctrine\ORM\Mapping\PostRemove) {
574 6
                $callbacks[] = array($method->name, Events::postRemove);
575
            }
576
577 21
            if ($annot instanceof \Doctrine\ORM\Mapping\PostLoad) {
578 11
                $callbacks[] = array($method->name, Events::postLoad);
579
            }
580
581 21
            if ($annot instanceof \Doctrine\ORM\Mapping\PreFlush) {
582 21
                $callbacks[] = array($method->name, Events::preFlush);
583
            }
584
        }
585
586 27
        return $callbacks;
587
    }
588
589
    /**
590
     * Parse the given JoinColumn as array
591
     *
592
     * @param JoinColumn $joinColumn
593
     * @return array
594
     */
595 174
    private function joinColumnToArray(JoinColumn $joinColumn)
596
    {
597
        return array(
598 174
            'name' => $joinColumn->name,
599 174
            'unique' => $joinColumn->unique,
600 174
            'nullable' => $joinColumn->nullable,
601 174
            'onDelete' => $joinColumn->onDelete,
602 174
            'columnDefinition' => $joinColumn->columnDefinition,
603 174
            'referencedColumnName' => $joinColumn->referencedColumnName,
604
        );
605
    }
606
607
    /**
608
     * Parse the given Column as array
609
     *
610
     * @param string $fieldName
611
     * @param Column $column
612
     *
613
     * @return array
614
     */
615 340
    private function columnToArray($fieldName, Column $column)
616
    {
617
        $mapping = array(
618 340
            'fieldName' => $fieldName,
619 340
            'type'      => $column->type,
620 340
            'scale'     => $column->scale,
621 340
            'length'    => $column->length,
622 340
            'unique'    => $column->unique,
623 340
            'nullable'  => $column->nullable,
624 340
            'precision' => $column->precision
625
        );
626
627 340
        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...
628 7
            $mapping['options'] = $column->options;
629
        }
630
631 340
        if (isset($column->name)) {
632 80
            $mapping['columnName'] = $column->name;
633
        }
634
635 340
        if (isset($column->columnDefinition)) {
636 5
            $mapping['columnDefinition'] = $column->columnDefinition;
637
        }
638
639 340
        return $mapping;
640
    }
641
642
    /**
643
     * Factory method for the Annotation Driver.
644
     *
645
     * @param array|string          $paths
646
     * @param AnnotationReader|null $reader
647
     *
648
     * @return AnnotationDriver
649
     */
650
    static public function create($paths = array(), AnnotationReader $reader = null)
651
    {
652
        if ($reader == null) {
653
            $reader = new AnnotationReader();
654
        }
655
656
        return new self($reader, $paths);
657
    }
658
}
659