Completed
Pull Request — master (#5938)
by Maximilian
15:28 queued 07:05
created

AnnotationDriver   D

Complexity

Total Complexity 110

Size/Duplication

Total Lines 620
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 96.05%

Importance

Changes 3
Bugs 2 Features 1
Metric Value
wmc 110
c 3
b 2
f 1
lcom 1
cbo 8
dl 0
loc 620
ccs 292
cts 304
cp 0.9605
rs 4.7916

6 Methods

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