Completed
Pull Request — master (#6151)
by Andy
09:42
created

AnnotationDriver   D

Complexity

Total Complexity 113

Size/Duplication

Total Lines 630
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 96.51%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 113
lcom 1
cbo 8
dl 0
loc 630
ccs 304
cts 315
cp 0.9651
rs 4.7506
c 1
b 0
f 0

6 Methods

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