Completed
Pull Request — master (#6481)
by Luís
15:30 queued 09:17
created

AnnotationDriver::joinColumnToArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 11
c 0
b 0
f 0
ccs 7
cts 7
cp 1
rs 9.4285
cc 1
eloc 8
nc 1
nop 1
crap 1
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM\Mapping\Driver;
21
22
use Doctrine\Common\Annotations\AnnotationReader;
23
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
24
use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver;
25
use Doctrine\ORM\Events;
26
use Doctrine\ORM\Mapping;
27
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
28
use Doctrine\ORM\Mapping\MappingException;
29
30
/**
31
 * The AnnotationDriver reads the mapping metadata from docblock annotations.
32
 *
33
 * @since 2.0
34
 * @author Benjamin Eberlei <[email protected]>
35
 * @author Guilherme Blanco <[email protected]>
36
 * @author Jonathan H. Wage <[email protected]>
37
 * @author Roman Borschel <[email protected]>
38
 */
39
class AnnotationDriver extends AbstractAnnotationDriver
40
{
41
    /**
42
     * {@inheritDoc}
43
     */
44
    protected $entityAnnotationClasses = [
45
        Mapping\Entity::class => 1,
46
        Mapping\MappedSuperclass::class => 2,
47
    ];
48
49
    /**
50
     * {@inheritDoc}
51
     */
52 357
    public function loadMetadataForClass($className, ClassMetadata $metadata)
53
    {
54
        /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */
55 357
        $class = $metadata->getReflectionClass();
56
57 357
        if ( ! $class) {
58
            // this happens when running annotation driver in combination with
59
            // static reflection services. This is not the nicest fix
60 1
            $class = new \ReflectionClass($metadata->name);
61
        }
62
63 357
        $classAnnotations = $this->reader->getClassAnnotations($class);
64
65 357
        if ($classAnnotations) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $classAnnotations of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
66 354
            foreach ($classAnnotations as $key => $annot) {
67 354
                if ( ! is_numeric($key)) {
68
                    continue;
69
                }
70
71 354
                $classAnnotations[get_class($annot)] = $annot;
72
            }
73
        }
74
75
        // Evaluate Entity annotation
76 357
        if (isset($classAnnotations[Mapping\Entity::class])) {
77 349
            $entityAnnot = $classAnnotations[Mapping\Entity::class];
78 349
            if ($entityAnnot->repositoryClass !== null) {
79 8
                $metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
80
            }
81
82 349
            if ($entityAnnot->readOnly) {
83 349
                $metadata->markReadOnly();
84
            }
85 42
        } else if (isset($classAnnotations[Mapping\MappedSuperclass::class])) {
86 31
            $mappedSuperclassAnnot = $classAnnotations[Mapping\MappedSuperclass::class];
87
88 31
            $metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass);
89 31
            $metadata->isMappedSuperclass = true;
90 13
        } else if (isset($classAnnotations[Mapping\Embeddable::class])) {
91 10
            $metadata->isEmbeddedClass = true;
92
        } else {
93 3
            throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
94
        }
95
96
        // Evaluate Table annotation
97 354
        if (isset($classAnnotations[Mapping\Table::class])) {
98 187
            $tableAnnot   = $classAnnotations[Mapping\Table::class];
99
            $primaryTable = [
100 187
                'name'   => $tableAnnot->name,
101 187
                'schema' => $tableAnnot->schema
102
            ];
103
104 187
            if ($tableAnnot->indexes !== null) {
105 14
                foreach ($tableAnnot->indexes as $indexAnnot) {
106 14
                    $index = ['columns' => $indexAnnot->columns];
107
108 14
                    if ( ! empty($indexAnnot->flags)) {
109 1
                        $index['flags'] = $indexAnnot->flags;
110
                    }
111
112 14
                    if ( ! empty($indexAnnot->options)) {
113 1
                        $index['options'] = $indexAnnot->options;
114
                    }
115
116 14
                    if ( ! empty($indexAnnot->name)) {
117 13
                        $primaryTable['indexes'][$indexAnnot->name] = $index;
118
                    } else {
119 14
                        $primaryTable['indexes'][] = $index;
120
                    }
121
                }
122
            }
123
124 187
            if ($tableAnnot->uniqueConstraints !== null) {
125 7
                foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) {
126 7
                    $uniqueConstraint = ['columns' => $uniqueConstraintAnnot->columns];
127
128 7
                    if ( ! empty($uniqueConstraintAnnot->options)) {
129 1
                        $uniqueConstraint['options'] = $uniqueConstraintAnnot->options;
130
                    }
131
132 7
                    if ( ! empty($uniqueConstraintAnnot->name)) {
133 7
                        $primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint;
134
                    } else {
135 7
                        $primaryTable['uniqueConstraints'][] = $uniqueConstraint;
136
                    }
137
                }
138
            }
139
140 187
            if ($tableAnnot->options) {
141 4
                $primaryTable['options'] = $tableAnnot->options;
142
            }
143
144 187
            $metadata->setPrimaryTable($primaryTable);
145
        }
146
147
        // Evaluate @Cache annotation
148 354
        if (isset($classAnnotations[Mapping\Cache::class])) {
149 16
            $cacheAnnot = $classAnnotations[Mapping\Cache::class];
150
            $cacheMap   = [
151 16
                'region' => $cacheAnnot->region,
152 16
                'usage'  => constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage),
153
            ];
154
155 16
            $metadata->enableCache($cacheMap);
156
        }
157
158
        // Evaluate NamedNativeQueries annotation
159 354
        if (isset($classAnnotations[Mapping\NamedNativeQueries::class])) {
160 16
            $namedNativeQueriesAnnot = $classAnnotations[Mapping\NamedNativeQueries::class];
161
162 16
            foreach ($namedNativeQueriesAnnot->value as $namedNativeQuery) {
163 16
                $metadata->addNamedNativeQuery(
164
                    [
165 16
                        'name'              => $namedNativeQuery->name,
166 16
                        'query'             => $namedNativeQuery->query,
167 16
                        'resultClass'       => $namedNativeQuery->resultClass,
168 16
                        'resultSetMapping'  => $namedNativeQuery->resultSetMapping,
169
                    ]
170
                );
171
            }
172
        }
173
174
        // Evaluate SqlResultSetMappings annotation
175 354
        if (isset($classAnnotations[Mapping\SqlResultSetMappings::class])) {
176 16
            $sqlResultSetMappingsAnnot = $classAnnotations[Mapping\SqlResultSetMappings::class];
177
178 16
            foreach ($sqlResultSetMappingsAnnot->value as $resultSetMapping) {
179 16
                $entities = [];
180 16
                $columns  = [];
181 16
                foreach ($resultSetMapping->entities as $entityResultAnnot) {
182
                    $entityResult = [
183 16
                        'fields'                => [],
184 16
                        'entityClass'           => $entityResultAnnot->entityClass,
185 16
                        'discriminatorColumn'   => $entityResultAnnot->discriminatorColumn,
186
                    ];
187
188 16
                    foreach ($entityResultAnnot->fields as $fieldResultAnnot) {
189 16
                        $entityResult['fields'][] = [
190 16
                            'name'      => $fieldResultAnnot->name,
191 16
                            'column'    => $fieldResultAnnot->column
192
                        ];
193
                    }
194
195 16
                    $entities[] = $entityResult;
196
                }
197
198 16
                foreach ($resultSetMapping->columns as $columnResultAnnot) {
199 10
                    $columns[] = [
200 10
                        'name' => $columnResultAnnot->name,
201
                    ];
202
                }
203
204 16
                $metadata->addSqlResultSetMapping(
205
                    [
206 16
                        'name'          => $resultSetMapping->name,
207 16
                        'entities'      => $entities,
208 16
                        'columns'       => $columns
209
                    ]
210
                );
211
            }
212
        }
213
214
        // Evaluate NamedQueries annotation
215 354
        if (isset($classAnnotations[Mapping\NamedQueries::class])) {
216 9
            $namedQueriesAnnot = $classAnnotations[Mapping\NamedQueries::class];
217
218 9
            if ( ! is_array($namedQueriesAnnot->value)) {
219
                throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations.");
220
            }
221
222 9
            foreach ($namedQueriesAnnot->value as $namedQuery) {
223 9
                if ( ! ($namedQuery instanceof Mapping\NamedQuery)) {
224
                    throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations.");
225
                }
226 9
                $metadata->addNamedQuery(
227
                    [
228 9
                        'name'  => $namedQuery->name,
229 9
                        'query' => $namedQuery->query
230
                    ]
231
                );
232
            }
233
        }
234
235
        // Evaluate InheritanceType annotation
236 354
        if (isset($classAnnotations[Mapping\InheritanceType::class])) {
237 67
            $inheritanceTypeAnnot = $classAnnotations[Mapping\InheritanceType::class];
238
239 67
            $metadata->setInheritanceType(
240 67
                constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAnnot->value)
241
            );
242
243 67
            if ($metadata->inheritanceType != Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) {
244
                // Evaluate DiscriminatorColumn annotation
245 67
                if (isset($classAnnotations[Mapping\DiscriminatorColumn::class])) {
246 51
                    $discrColumnAnnot = $classAnnotations[Mapping\DiscriminatorColumn::class];
247
248 51
                    $metadata->setDiscriminatorColumn(
249
                        [
250 51
                            'name'             => $discrColumnAnnot->name,
251 51
                            'type'             => $discrColumnAnnot->type ?: 'string',
252 51
                            'length'           => $discrColumnAnnot->length ?: 255,
253 51
                            'columnDefinition' => $discrColumnAnnot->columnDefinition,
254
                        ]
255
                    );
256
                } else {
257 19
                    $metadata->setDiscriminatorColumn(['name' => 'dtype', 'type' => 'string', 'length' => 255]);
258
                }
259
260
                // Evaluate DiscriminatorMap annotation
261 67
                if (isset($classAnnotations[Mapping\DiscriminatorMap::class])) {
262 66
                    $discrMapAnnot = $classAnnotations[Mapping\DiscriminatorMap::class];
263 66
                    $metadata->setDiscriminatorMap($discrMapAnnot->value);
264
                }
265
            }
266
        }
267
268
269
        // Evaluate DoctrineChangeTrackingPolicy annotation
270 354
        if (isset($classAnnotations[Mapping\ChangeTrackingPolicy::class])) {
271 5
            $changeTrackingAnnot = $classAnnotations[Mapping\ChangeTrackingPolicy::class];
272 5
            $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' . $changeTrackingAnnot->value));
273
        }
274
275
        // Evaluate annotations on properties/fields
276
        /* @var $property \ReflectionProperty */
277 354
        foreach ($class->getProperties() as $property) {
278 352
            if ($metadata->isMappedSuperclass && ! $property->isPrivate()
279
                ||
280 352
                $metadata->isInheritedField($property->name)
281
                ||
282 352
                $metadata->isInheritedAssociation($property->name)
283
                ||
284 352
                $metadata->isInheritedEmbeddedClass($property->name)) {
285 70
                continue;
286
            }
287
288 352
            $mapping = [];
289 352
            $mapping['fieldName'] = $property->getName();
290
291
            // Evaluate @Cache annotation
292 352
            if (($cacheAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Cache::class)) !== null) {
293 13
                $mapping['cache'] = $metadata->getAssociationCacheDefaults(
294 13
                    $mapping['fieldName'],
295
                    [
296 13
                        'usage'  => constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage),
297 13
                        'region' => $cacheAnnot->region,
298
                    ]
299
                );
300
            }
301
            // Check for JoinColumn/JoinColumns annotations
302 351
            $joinColumns = [];
303
304 351
            if ($joinColumnAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinColumn::class)) {
305 136
                $joinColumns[] = $this->joinColumnToArray($joinColumnAnnot);
306 350
            } else if ($joinColumnsAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinColumns::class)) {
307 18
                foreach ($joinColumnsAnnot->value as $joinColumn) {
308 18
                    $joinColumns[] = $this->joinColumnToArray($joinColumn);
309
                }
310
            }
311
312
            // Field can only be annotated with one of:
313
            // @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany
314 351
            if ($columnAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Column::class)) {
315 343
                if ($columnAnnot->type == null) {
316
                    throw MappingException::propertyTypeIsRequired($className, $property->getName());
317
                }
318
319 343
                $mapping = $this->columnToArray($property->getName(), $columnAnnot);
320
321 343
                if ($idAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Id::class)) {
0 ignored issues
show
Unused Code introduced by
$idAnnot is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
322 336
                    $mapping['id'] = true;
323
                }
324
325 343
                if ($generatedValueAnnot = $this->reader->getPropertyAnnotation($property, Mapping\GeneratedValue::class)) {
326 286
                    $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $generatedValueAnnot->strategy));
327
                }
328
329 343
                if ($this->reader->getPropertyAnnotation($property, Mapping\Version::class)) {
330 12
                    $metadata->setVersionMapping($mapping);
331
                }
332
333 343
                $metadata->mapField($mapping);
334
335
                // Check for SequenceGenerator/TableGenerator definition
336 343
                if ($seqGeneratorAnnot = $this->reader->getPropertyAnnotation($property, Mapping\SequenceGenerator::class)) {
337 7
                    $metadata->setSequenceGeneratorDefinition(
338
                        [
339 7
                            'sequenceName' => $seqGeneratorAnnot->sequenceName,
340 7
                            'allocationSize' => $seqGeneratorAnnot->allocationSize,
341 7
                            'initialValue' => $seqGeneratorAnnot->initialValue
342
                        ]
343
                    );
344 340
                } else if ($this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\TableGenerator')) {
345
                    throw MappingException::tableIdGeneratorNotImplemented($className);
346 340
                } else if ($customGeneratorAnnot = $this->reader->getPropertyAnnotation($property, Mapping\CustomIdGenerator::class)) {
347 2
                    $metadata->setCustomGeneratorDefinition(
348
                        [
349 343
                            'class' => $customGeneratorAnnot->class
350
                        ]
351
                    );
352
                }
353 250
            } else if ($oneToOneAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OneToOne::class)) {
354 108
                if ($idAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Id::class)) {
0 ignored issues
show
Unused Code introduced by
$idAnnot is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
355 9
                    $mapping['id'] = true;
356
                }
357
358 108
                $mapping['targetEntity'] = $oneToOneAnnot->targetEntity;
359 108
                $mapping['joinColumns'] = $joinColumns;
360 108
                $mapping['mappedBy'] = $oneToOneAnnot->mappedBy;
361 108
                $mapping['inversedBy'] = $oneToOneAnnot->inversedBy;
362 108
                $mapping['cascade'] = $oneToOneAnnot->cascade;
363 108
                $mapping['orphanRemoval'] = $oneToOneAnnot->orphanRemoval;
364 108
                $mapping['fetch'] = $this->getFetchMode($className, $oneToOneAnnot->fetch);
365 108
                $metadata->mapOneToOne($mapping);
366 200
            } else if ($oneToManyAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OneToMany::class)) {
367 101
                $mapping['mappedBy'] = $oneToManyAnnot->mappedBy;
368 101
                $mapping['targetEntity'] = $oneToManyAnnot->targetEntity;
369 101
                $mapping['cascade'] = $oneToManyAnnot->cascade;
370 101
                $mapping['indexBy'] = $oneToManyAnnot->indexBy;
371 101
                $mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval;
372 101
                $mapping['fetch'] = $this->getFetchMode($className, $oneToManyAnnot->fetch);
373
374 101
                if ($orderByAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OrderBy::class)) {
375 14
                    $mapping['orderBy'] = $orderByAnnot->value;
376
                }
377
378 101
                $metadata->mapOneToMany($mapping);
379 197
            } else if ($manyToOneAnnot = $this->reader->getPropertyAnnotation($property, Mapping\ManyToOne::class)) {
380 127
                if ($idAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Id::class)) {
0 ignored issues
show
Unused Code introduced by
$idAnnot is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
381 27
                    $mapping['id'] = true;
382
                }
383
384 127
                $mapping['joinColumns'] = $joinColumns;
385 127
                $mapping['cascade'] = $manyToOneAnnot->cascade;
386 127
                $mapping['inversedBy'] = $manyToOneAnnot->inversedBy;
387 127
                $mapping['targetEntity'] = $manyToOneAnnot->targetEntity;
388 127
                $mapping['fetch'] = $this->getFetchMode($className, $manyToOneAnnot->fetch);
389 127
                $metadata->mapManyToOne($mapping);
390 114
            } else if ($manyToManyAnnot = $this->reader->getPropertyAnnotation($property, Mapping\ManyToMany::class)) {
391 85
                $joinTable = [];
392
393 85
                if ($joinTableAnnot = $this->reader->getPropertyAnnotation($property, Mapping\JoinTable::class)) {
394
                    $joinTable = [
395 65
                        'name' => $joinTableAnnot->name,
396 65
                        'schema' => $joinTableAnnot->schema
397
                    ];
398
399 65
                    foreach ($joinTableAnnot->joinColumns as $joinColumn) {
400 64
                        $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn);
401
                    }
402
403 65
                    foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) {
404 64
                        $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn);
405
                    }
406
                }
407
408 85
                $mapping['joinTable'] = $joinTable;
409 85
                $mapping['targetEntity'] = $manyToManyAnnot->targetEntity;
410 85
                $mapping['mappedBy'] = $manyToManyAnnot->mappedBy;
411 85
                $mapping['inversedBy'] = $manyToManyAnnot->inversedBy;
412 85
                $mapping['cascade'] = $manyToManyAnnot->cascade;
413 85
                $mapping['indexBy'] = $manyToManyAnnot->indexBy;
414 85
                $mapping['orphanRemoval'] = $manyToManyAnnot->orphanRemoval;
415 85
                $mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnot->fetch);
416
417 85
                if ($orderByAnnot = $this->reader->getPropertyAnnotation($property, Mapping\OrderBy::class)) {
418 3
                    $mapping['orderBy'] = $orderByAnnot->value;
419
                }
420
421 85
                $metadata->mapManyToMany($mapping);
422 49
            } else if ($embeddedAnnot = $this->reader->getPropertyAnnotation($property, Mapping\Embedded::class)) {
423 12
                $mapping['class'] = $embeddedAnnot->class;
424 12
                $mapping['columnPrefix'] = $embeddedAnnot->columnPrefix;
425
426 350
                $metadata->mapEmbedded($mapping);
427
            }
428
        }
429
430
        // Evaluate AssociationOverrides annotation
431 352
        if (isset($classAnnotations[Mapping\AssociationOverrides::class])) {
432 5
            $associationOverridesAnnot = $classAnnotations[Mapping\AssociationOverrides::class];
433
434 5
            foreach ($associationOverridesAnnot->value as $associationOverride) {
435 5
                $override   = [];
436 5
                $fieldName  = $associationOverride->name;
437
438
                // Check for JoinColumn/JoinColumns annotations
439 5
                if ($associationOverride->joinColumns) {
440 3
                    $joinColumns = [];
441
442 3
                    foreach ($associationOverride->joinColumns as $joinColumn) {
443 3
                        $joinColumns[] = $this->joinColumnToArray($joinColumn);
444
                    }
445
446 3
                    $override['joinColumns'] = $joinColumns;
447
                }
448
449
                // Check for JoinTable annotations
450 5
                if ($associationOverride->joinTable) {
451 2
                    $joinTableAnnot = $associationOverride->joinTable;
452
                    $joinTable      = [
453 2
                        'name'      => $joinTableAnnot->name,
454 2
                        'schema'    => $joinTableAnnot->schema
455
                    ];
456
457 2
                    foreach ($joinTableAnnot->joinColumns as $joinColumn) {
458 2
                        $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn);
459
                    }
460
461 2
                    foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) {
462 2
                        $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn);
463
                    }
464
465 2
                    $override['joinTable'] = $joinTable;
466
                }
467
468
                // Check for inversedBy
469 5
                if ($associationOverride->inversedBy) {
470 1
                    $override['inversedBy'] = $associationOverride->inversedBy;
471
                }
472
473
                // Check for `fetch`
474 5
                if ($associationOverride->fetch) {
475 1
                    $override['fetch'] = constant(Mapping\ClassMetadata::class . '::FETCH_' . $associationOverride->fetch);
476
                }
477
478 5
                $metadata->setAssociationOverride($fieldName, $override);
479
            }
480
        }
481
482
        // Evaluate AttributeOverrides annotation
483 352
        if (isset($classAnnotations[Mapping\AttributeOverrides::class])) {
484 3
            $attributeOverridesAnnot = $classAnnotations[Mapping\AttributeOverrides::class];
485
486 3
            foreach ($attributeOverridesAnnot->value as $attributeOverrideAnnot) {
487 3
                $attributeOverride = $this->columnToArray($attributeOverrideAnnot->name, $attributeOverrideAnnot->column);
488
489 3
                $metadata->setAttributeOverride($attributeOverrideAnnot->name, $attributeOverride);
490
            }
491
        }
492
493
        // Evaluate EntityListeners annotation
494 352
        if (isset($classAnnotations[Mapping\EntityListeners::class])) {
495 10
            $entityListenersAnnot = $classAnnotations[Mapping\EntityListeners::class];
496
497 10
            foreach ($entityListenersAnnot->value as $item) {
498 10
                $listenerClassName = $metadata->fullyQualifiedClassName($item);
499
500 10
                if ( ! class_exists($listenerClassName)) {
501
                    throw MappingException::entityListenerClassNotFound($listenerClassName, $className);
502
                }
503
504 10
                $hasMapping     = false;
505 10
                $listenerClass  = new \ReflectionClass($listenerClassName);
506
507
                /* @var $method \ReflectionMethod */
508 10
                foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
509
                    // find method callbacks.
510 10
                    $callbacks  = $this->getMethodCallbacks($method);
511 10
                    $hasMapping = $hasMapping ?: ( ! empty($callbacks));
512
513 10
                    foreach ($callbacks as $value) {
514 10
                        $metadata->addEntityListener($value[1], $listenerClassName, $value[0]);
515
                    }
516
                }
517
518
                // Evaluate the listener using naming convention.
519 10
                if ( ! $hasMapping ) {
520 10
                    EntityListenerBuilder::bindEntityListener($metadata, $listenerClassName);
0 ignored issues
show
Compatibility introduced by
$metadata of type object<Doctrine\ORM\Mapping\ClassMetadataInfo> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a child class of the class Doctrine\ORM\Mapping\ClassMetadataInfo to be always present.

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

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

Loading history...
521
                }
522
            }
523
        }
524
525
        // Evaluate @HasLifecycleCallbacks annotation
526 352
        if (isset($classAnnotations[Mapping\HasLifecycleCallbacks::class])) {
527
            /* @var $method \ReflectionMethod */
528 17
            foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
529 16
                foreach ($this->getMethodCallbacks($method) as $value) {
530 16
                    $metadata->addLifecycleCallback($value[0], $value[1]);
531
                }
532
            }
533
        }
534 352
    }
535
536
    /**
537
     * Attempts to resolve the fetch mode.
538
     *
539
     * @param string $className The class name.
540
     * @param string $fetchMode The fetch mode.
541
     *
542
     * @return integer The fetch mode as defined in ClassMetadata.
543
     *
544
     * @throws MappingException If the fetch mode is not valid.
545
     */
546 236
    private function getFetchMode($className, $fetchMode)
547
    {
548 236
        if ( ! defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode)) {
549
            throw MappingException::invalidFetchMode($className, $fetchMode);
550
        }
551
552 236
        return constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode);
553
    }
554
555
    /**
556
     * Parses the given method.
557
     *
558
     * @param \ReflectionMethod $method
559
     *
560
     * @return array
561
     */
562 26
    private function getMethodCallbacks(\ReflectionMethod $method)
563
    {
564 26
        $callbacks   = [];
565 26
        $annotations = $this->reader->getMethodAnnotations($method);
566
567 26
        foreach ($annotations as $annot) {
568 20
            if ($annot instanceof Mapping\PrePersist) {
569 13
                $callbacks[] = [$method->name, Events::prePersist];
570
            }
571
572 20
            if ($annot instanceof Mapping\PostPersist) {
573 9
                $callbacks[] = [$method->name, Events::postPersist];
574
            }
575
576 20
            if ($annot instanceof Mapping\PreUpdate) {
577 11
                $callbacks[] = [$method->name, Events::preUpdate];
578
            }
579
580 20
            if ($annot instanceof Mapping\PostUpdate) {
581 7
                $callbacks[] = [$method->name, Events::postUpdate];
582
            }
583
584 20
            if ($annot instanceof Mapping\PreRemove) {
585 8
                $callbacks[] = [$method->name, Events::preRemove];
586
            }
587
588 20
            if ($annot instanceof Mapping\PostRemove) {
589 6
                $callbacks[] = [$method->name, Events::postRemove];
590
            }
591
592 20
            if ($annot instanceof Mapping\PostLoad) {
593 11
                $callbacks[] = [$method->name, Events::postLoad];
594
            }
595
596 20
            if ($annot instanceof Mapping\PreFlush) {
597 20
                $callbacks[] = [$method->name, Events::preFlush];
598
            }
599
        }
600
601 26
        return $callbacks;
602
    }
603
604
    /**
605
     * Parse the given JoinColumn as array
606
     *
607
     * @param Mapping\JoinColumn $joinColumn
608
     * @return array
609
     */
610 169
    private function joinColumnToArray(Mapping\JoinColumn $joinColumn)
611
    {
612
        return [
613 169
            'name' => $joinColumn->name,
614 169
            'unique' => $joinColumn->unique,
615 169
            'nullable' => $joinColumn->nullable,
616 169
            'onDelete' => $joinColumn->onDelete,
617 169
            'columnDefinition' => $joinColumn->columnDefinition,
618 169
            'referencedColumnName' => $joinColumn->referencedColumnName,
619
        ];
620
    }
621
622
    /**
623
     * Parse the given Column as array
624
     *
625
     * @param string $fieldName
626
     * @param Mapping\Column $column
627
     *
628
     * @return array
629
     */
630 343
    private function columnToArray($fieldName, Mapping\Column $column)
631
    {
632
        $mapping = [
633 343
            'fieldName' => $fieldName,
634 343
            'type'      => $column->type,
635 343
            'scale'     => $column->scale,
636 343
            'length'    => $column->length,
637 343
            'unique'    => $column->unique,
638 343
            'nullable'  => $column->nullable,
639 343
            'precision' => $column->precision
640
        ];
641
642 343
        if ($column->options) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $column->options of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
643 6
            $mapping['options'] = $column->options;
644
        }
645
646 343
        if (isset($column->name)) {
647 75
            $mapping['columnName'] = $column->name;
648
        }
649
650 343
        if (isset($column->columnDefinition)) {
651 4
            $mapping['columnDefinition'] = $column->columnDefinition;
652
        }
653
654 343
        return $mapping;
655
    }
656
657
    /**
658
     * Factory method for the Annotation Driver.
659
     *
660
     * @param array|string          $paths
661
     * @param AnnotationReader|null $reader
662
     *
663
     * @return AnnotationDriver
664
     */
665
    static public function create($paths = [], AnnotationReader $reader = null)
666
    {
667
        if ($reader == null) {
668
            $reader = new AnnotationReader();
669
        }
670
671
        return new self($reader, $paths);
672
    }
673
}
674