Test Failed
Push — develop ( 01a8a8...14ce66 )
by Guilherme
65:10
created

AnnotationDriver::getClassAnnotations()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 14
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 14
loc 14
rs 9.4285
c 0
b 0
f 0
ccs 0
cts 0
cp 0
cc 3
eloc 7
nc 3
nop 1
crap 12
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM\Mapping\Driver;
6
7
use Doctrine\Common\Annotations\AnnotationReader;
8
use Doctrine\DBAL\Types\Type;
9
use Doctrine\ORM\Annotation;
10
use Doctrine\ORM\Events;
11
use Doctrine\ORM\Mapping;
12
13
/**
14
 * The AnnotationDriver reads the mapping metadata from docblock annotations.
15
 *
16
 * @since 2.0
17
 * @author Benjamin Eberlei <[email protected]>
18
 * @author Guilherme Blanco <[email protected]>
19
 * @author Jonathan H. Wage <[email protected]>
20
 * @author Roman Borschel <[email protected]>
21
 */
22
class AnnotationDriver implements MappingDriver
23
{
24
    /**
25
     * {@inheritDoc}
26
     */
27
    protected $entityAnnotationClasses = [
28
        Annotation\Entity::class           => 1,
29
        Annotation\MappedSuperclass::class => 2,
30
    ];
31
32
    /**
33
     * The AnnotationReader.
34
     *
35
     * @var AnnotationReader
36
     */
37
    protected $reader;
38
39
    /**
40
     * The paths where to look for mapping files.
41
     *
42
     * @var array
43
     */
44
    protected $paths = [];
45
46
    /**
47
     * The paths excluded from path where to look for mapping files.
48
     *
49
     * @var array
50
     */
51
    protected $excludePaths = [];
52
53
    /**
54
     * The file extension of mapping documents.
55
     *
56 362
     * @var string
57
     */
58
    protected $fileExtension = '.php';
59 362
60 362
    /**
61
     * Cache for AnnotationDriver#getAllClassNames().
62 362
     *
63
     * @var array|null
64
     */
65 1
    protected $classNames;
66
67
    /**
68 362
     * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
69
     * docblock annotations.
70 362
     *
71 359
     * @param AnnotationReader  $reader The AnnotationReader to use, duck-typed.
72 359
     * @param string|array|null $paths  One or multiple paths where mapping classes can be found.
73
     */
74
    public function __construct($reader, $paths = null)
75
    {
76 359
        $this->reader = $reader;
77
        if ($paths) {
78
            $this->addPaths((array) $paths);
79
        }
80
    }
81
82 362
    /**
83 354
     * Appends lookup paths to metadata driver.
84
     *
85 354
     * @param array $paths
86 8
     *
87
     * @return void
88
     */
89 354
    public function addPaths(array $paths)
90 1
    {
91
        $this->paths = array_unique(array_merge($this->paths, $paths));
92
    }
93 354
94
    /**
95 38
     * Retrieves the defined metadata lookup paths.
96 32
     *
97
     * @return array
98 32
     */
99 32
    public function getPaths()
100 32
    {
101
        return $this->paths;
102 7
    }
103 4
104 4
    /**
105
     * Append exclude lookup paths to metadata driver.
106
     *
107 3
     * @param array $paths
108
     */
109
    public function addExcludePaths(array $paths)
110
    {
111 359
        $this->excludePaths = array_unique(array_merge($this->excludePaths, $paths));
112 187
    }
113
114 187
    /**
115
     * Retrieve the defined metadata lookup exclude paths.
116 187
     *
117 13
     * @return array
118 13
     */
119 13
    public function getExcludePaths()
120 13
    {
121 13
        return $this->excludePaths;
122
    }
123
124
    /**
125 187
     * Retrieve the current annotation reader
126 8
     *
127 8
     * @return AnnotationReader
128 8
     */
129 8
    public function getReader()
130
    {
131
        return $this->reader;
132
    }
133
134
    /**
135 359
     * Gets the file extension used to look for mapping files under.
136 14
     *
137
     * @return string
138 14
     */
139 14
    public function getFileExtension()
140 14
    {
141
        return $this->fileExtension;
142
    }
143
144
    /**
145 359
     * Sets the file extension used to look for mapping files under.
146 14
     *
147
     * @param string $fileExtension The file extension to set.
148 14
     *
149 14
     * @return void
150 14
     */
151 14
    public function setFileExtension($fileExtension)
152 14
    {
153 14
        $this->fileExtension = $fileExtension;
154
    }
155
156
    /**
157
     * Returns whether the class with the specified name is transient. Only non-transient
158
     * classes, that is entities and mapped superclasses, should have their metadata loaded.
159 359
     *
160 14
     * A class is non-transient if it is annotated with an annotation
161
     * from the {@see AnnotationDriver::entityAnnotationClasses}.
162 14
     *
163 14
     * @param string $className
164 14
     *
165
     * @return boolean
166 14
     */
167
    public function isTransient($className)
168 14
    {
169 14
        $classAnnotations = $this->reader->getClassAnnotations(new \ReflectionClass($className));
170 14
171
        foreach ($classAnnotations as $annot) {
172
            if (isset($this->entityAnnotationClasses[get_class($annot)])) {
173 14
                return false;
174 14
            }
175 14
        }
176 14
        return true;
177
    }
178
179
    /**
180 14
     * {@inheritDoc}
181
     */
182
    public function getAllClassNames()
183 14
    {
184 9
        if ($this->classNames !== null) {
185 9
            return $this->classNames;
186
        }
187
188
        if (!$this->paths) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->paths 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...
189 14
            throw Mapping\MappingException::pathRequired();
190 14
        }
191 14
192 14
        $classes = [];
193
        $includedFiles = [];
194
195
        foreach ($this->paths as $path) {
196
            if ( ! is_dir($path)) {
197
                throw Mapping\MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
198 359
            }
199 9
200
            $iterator = new \RegexIterator(
201 9
                new \RecursiveIteratorIterator(
202
                    new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
203
                    \RecursiveIteratorIterator::LEAVES_ONLY
204
                ),
205 9
                '/^.+' . preg_quote($this->fileExtension) . '$/i',
206 9
                \RecursiveRegexIterator::GET_MATCH
207
            );
208
209
            foreach ($iterator as $file) {
210 9
                $sourceFile = $file[0];
211
212
                if ( ! preg_match('(^phar:)i', $sourceFile)) {
213
                    $sourceFile = realpath($sourceFile);
214
                }
215 359
216 65
                foreach ($this->excludePaths as $excludePath) {
217
                    $exclude = str_replace('\\', '/', realpath($excludePath));
218 65
                    $current = str_replace('\\', '/', $sourceFile);
219 65
220
                    if (strpos($current, $exclude) !== false) {
221
                        continue 2;
222 65
                    }
223 65
                }
224
225 65
                require_once $sourceFile;
226
227
                $includedFiles[] = $sourceFile;
228 65
            }
229 48
        }
230
231 48
        $declared = get_declared_classes();
232 48
233 48
        foreach ($declared as $className) {
234 48
            $rc = new \ReflectionClass($className);
235
            $sourceFile = $rc->getFileName();
236 20
            if (in_array($sourceFile, $includedFiles) && ! $this->isTransient($className)) {
237 20
                $classes[] = $className;
238 20
            }
239
        }
240
241 65
        $this->classNames = $classes;
242
243
        return $classes;
244 65
    }
245 64
246
    /**
247 64
     * {@inheritDoc}
248
     */
249
    public function loadMetadataForClass(
250
        string $className,
251
        Mapping\ClassMetadata $metadata,
252
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
253
    ) : Mapping\ClassMetadata
254 359
    {
255 5
        $reflectionClass = $metadata->getReflectionClass();
256
257 5
        if (! $reflectionClass) {
258 5
            // this happens when running annotation driver in combination with
259
            // static reflection services. This is not the nicest fix
260
            $reflectionClass = new \ReflectionClass($metadata->getClassName());
261
        }
262
263
        $classAnnotations = $this->getClassAnnotations($reflectionClass);
264 359
        $classMetadata    = $this->convertClassAnnotationsToClassMetadata(
265
            $classAnnotations,
266
            $reflectionClass,
267
            $metadata
268
        );
269 357
270 357
        // Evaluate Table annotation
271 357 View Code Duplication
        if (isset($classAnnotations[Annotation\Table::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
272 357
            $tableAnnot = $classAnnotations[Annotation\Table::class];
273 69
            $table      = $this->convertTableAnnotationToTableMetadata($tableAnnot);
274
275
            $classMetadata->setTable($table);
276 357
        }
277 357
278
        // Evaluate @Cache annotation
279 View Code Duplication
        if (isset($classAnnotations[Annotation\Cache::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
280 357
            $cacheAnnot = $classAnnotations[Annotation\Cache::class];
281 12
            $cache      = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $metadata);
282 12
283 12
            $classMetadata->setCache($cache);
284
        }
285
286
        // Evaluate NamedNativeQueries annotation
287 356 View Code Duplication
        if (isset($classAnnotations[Annotation\NamedNativeQueries::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
288
            $namedNativeQueriesAnnot = $classAnnotations[Annotation\NamedNativeQueries::class];
289 356
290 136
            foreach ($namedNativeQueriesAnnot->value as $namedNativeQuery) {
291 355
                $classMetadata->addNamedNativeQuery(
292 21
                    $namedNativeQuery->name,
293 21
                    $namedNativeQuery->query,
294
                    [
295
                        'resultClass'      => $namedNativeQuery->resultClass,
296
                        'resultSetMapping' => $namedNativeQuery->resultSetMapping,
297
                    ]
298
                );
299 356
            }
300 347
        }
301
302
        // Evaluate SqlResultSetMappings annotation
303 View Code Duplication
        if (isset($classAnnotations[Annotation\SqlResultSetMappings::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
304 347
            $sqlResultSetMappingsAnnot = $classAnnotations[Annotation\SqlResultSetMappings::class];
305
306 347
            foreach ($sqlResultSetMappingsAnnot->value as $resultSetMapping) {
307 340
                $sqlResultSetMapping = $this->convertSqlResultSetMapping($resultSetMapping);
308
309
                $classMetadata->addSqlResultSetMapping($sqlResultSetMapping);
310 347
            }
311 291
        }
312 291
313
        // Evaluate NamedQueries annotation
314 View Code Duplication
        if (isset($classAnnotations[Annotation\NamedQueries::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
315
            $namedQueriesAnnot = $classAnnotations[Annotation\NamedQueries::class];
316 347
317
            if ( ! is_array($namedQueriesAnnot->value)) {
318 347
                throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations.");
319 13
            }
320
321
            foreach ($namedQueriesAnnot->value as $namedQuery) {
322
                if ( ! ($namedQuery instanceof Annotation\NamedQuery)) {
323 347
                    throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations.");
324 8
                }
325 8
326 8
                $classMetadata->addNamedQuery($namedQuery->name, $namedQuery->query);
327 8
            }
328
        }
329 344
330
        // Evaluate InheritanceType annotation
331 344
        if (isset($classAnnotations[Annotation\InheritanceType::class])) {
332 2
            $inheritanceTypeAnnot = $classAnnotations[Annotation\InheritanceType::class];
333 347
334
            $classMetadata->setInheritanceType(
335
                constant(sprintf('%s::%s', Mapping\InheritanceType::class, $inheritanceTypeAnnot->value))
336 255
            );
337 106
338 9
            if ($classMetadata->inheritanceType !== Mapping\InheritanceType::NONE) {
339
                $discriminatorColumn = new Mapping\DiscriminatorColumnMetadata();
340
341 106
                $discriminatorColumn->setTableName($classMetadata->getTableName());
342 106
                $discriminatorColumn->setColumnName('dtype');
343 106
                $discriminatorColumn->setType(Type::getType('string'));
344 106
                $discriminatorColumn->setLength(255);
345 106
346 106
                // Evaluate DiscriminatorColumn annotation
347 106 View Code Duplication
                if (isset($classAnnotations[Annotation\DiscriminatorColumn::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
348 106
                    /** @var Annotation\DiscriminatorColumn $discriminatorColumnAnnotation */
349 206
                    $discriminatorColumnAnnotation = $classAnnotations[Annotation\DiscriminatorColumn::class];
350 102
                    $typeName                      = ! empty($discriminatorColumnAnnotation->type)
351 102
                        ? $discriminatorColumnAnnotation->type
352 102
                        : 'string';
353 102
354 102
                    $discriminatorColumn->setType(Type::getType($typeName));
355 102
                    $discriminatorColumn->setColumnName($discriminatorColumnAnnotation->name);
356
357 102
                    if (! empty($discriminatorColumnAnnotation->columnDefinition)) {
358 15
                        $discriminatorColumn->setColumnDefinition($discriminatorColumnAnnotation->columnDefinition);
359
                    }
360
361 102
                    if (! empty($discriminatorColumnAnnotation->length)) {
362 203
                        $discriminatorColumn->setLength($discriminatorColumnAnnotation->length);
363 133
                    }
364 30
                }
365
366
                $classMetadata->setDiscriminatorColumn($discriminatorColumn);
367 133
368 133
                // Evaluate DiscriminatorMap annotation
369 133 View Code Duplication
                if (isset($classAnnotations[Annotation\DiscriminatorMap::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
370 133
                    $discriminatorMapAnnotation = $classAnnotations[Annotation\DiscriminatorMap::class];
371 133
                    $discriminatorMap           = array_map(
372 133
                        function ($className) use ($classMetadata) {
373 113
                            return $classMetadata->fullyQualifiedClassName($className);
374 84
                        },
375
                        $discriminatorMapAnnotation->value
376 84
                    );
377
378 65
                    $classMetadata->setDiscriminatorMap($discriminatorMap);
379 65
                }
380
            }
381
        }
382 65
383 64
        // Evaluate @ChangeTrackingPolicy annotation
384 View Code Duplication
        if (isset($classAnnotations[Annotation\ChangeTrackingPolicy::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
385
            $changeTrackingAnnot = $classAnnotations[Annotation\ChangeTrackingPolicy::class];
386 65
387 64
            $classMetadata->setChangeTrackingPolicy(
388
                constant(sprintf('%s::%s', Mapping\ChangeTrackingPolicy::class, $changeTrackingAnnot->value))
389
            );
390
        }
391 84
392 84
        // Evaluate @EntityListeners annotation
393 84 View Code Duplication
        if (isset($classAnnotations[Annotation\EntityListeners::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
394 84
            /** @var Annotation\EntityListeners $entityListenersAnnot */
395 84
            $entityListenersAnnot = $classAnnotations[Annotation\EntityListeners::class];
396 84
397 84
            foreach ($entityListenersAnnot->value as $item) {
398 84
                $listenerClassName = $metadata->fullyQualifiedClassName($item);
399
400 84
                if (! class_exists($listenerClassName)) {
401 3
                    throw Mapping\MappingException::entityListenerClassNotFound(
402
                        $listenerClassName,
403
                        $classMetadata->getClassName()
404 84
                    );
405 44
                }
406 10
407 10
                $listenerClass = new \ReflectionClass($listenerClassName);
408
409 355
                /* @var $method \ReflectionMethod */
410
                foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
411
                    foreach ($this->getMethodCallbacks($method) as $callback) {
412
                        $classMetadata->addEntityListener($callback, $listenerClassName, $method->getName());
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
413
                    }
414 357
                }
415 4
            }
416
        }
417 4
418 4
        // Evaluate @HasLifecycleCallbacks annotation
419 4 View Code Duplication
        if (isset($classAnnotations[Annotation\HasLifecycleCallbacks::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
420
            /* @var $method \ReflectionMethod */
421
            foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
422 4
                foreach ($this->getMethodCallbacks($method) as $callback) {
423 3
                    $classMetadata->addLifecycleCallback($method->getName(), $callback);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
424
                }
425 3
            }
426 3
        }
427
428
        // Evaluate annotations on properties/fields
429 3
        /* @var $reflProperty \ReflectionProperty */
430
        foreach ($reflectionClass->getProperties() as $reflProperty) {
431
            if ($reflProperty->getDeclaringClass()->getName() !== $reflectionClass->getName()) {
0 ignored issues
show
introduced by
Consider using $reflProperty->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
Bug introduced by
Consider using $reflectionClass->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
432
                continue;
433 4
            }
434 2
435
            $propertyAnnotations = $this->getPropertyAnnotations($reflProperty);
436 2
437 2
            // Field can only be annotated with one of:
438
            // @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany, @Embedded
0 ignored issues
show
Unused Code Comprehensibility introduced by
48% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
439
            switch (true) {
440 2
                case isset($propertyAnnotations[Annotation\Column::class]):
441 2
                    // Field found
442
                    $fieldMetadata = $this->convertReflectionPropertyToFieldMetadata(
443
                        $reflProperty,
444 2
                        $propertyAnnotations
445 2
                    );
446
447
                    $classMetadata->addProperty($fieldMetadata);
448 2
                    break;
449
450
                case isset($propertyAnnotations[Annotation\OneToOne::class]):
451
                    $assocMetadata = $this->convertReflectionPropertyToOneToOneAssociationMetadata(
452 4
                        $reflProperty,
453 1
                        $propertyAnnotations,
454
                        $metadata
455
                    );
456 4
457
                    $classMetadata->addProperty($assocMetadata);
458
                    break;
459
460
                case isset($propertyAnnotations[Annotation\ManyToOne::class]):
461 357
                    $assocMetadata = $this->convertReflectionPropertyToManyToOneAssociationMetadata(
462 3
                        $reflProperty,
463
                        $propertyAnnotations,
464 3
                        $metadata
465 3
                    );
466
467 3
                    $classMetadata->addProperty($assocMetadata);
468
                    break;
469
470
                case isset($propertyAnnotations[Annotation\OneToMany::class]):
471
                    $assocMetadata = $this->convertReflectionPropertyToOneToManyAssociationMetadata(
472 357
                        $reflProperty,
473 10
                        $propertyAnnotations,
474
                        $metadata
475 10
                    );
476 10
477
                    $classMetadata->addProperty($assocMetadata);
478 10
                    break;
479
480
                case isset($propertyAnnotations[Annotation\ManyToMany::class]):
481
                    $assocMetadata = $this->convertReflectionPropertyToManyToManyAssociationMetadata(
482 10
                        $reflProperty,
483 10
                        $propertyAnnotations,
484
                        $metadata
485
                    );
486 10
487
                    $classMetadata->addProperty($assocMetadata);
488 10
                    break;
489 10
490
                case isset($propertyAnnotations[Annotation\Embedded::class]):
491 10
                    $embeddedAnnot = $propertyAnnotations[Annotation\Embedded::class];
492 10
493
                    $mapping['fieldName']    = $reflProperty->getName();
0 ignored issues
show
Coding Style Comprehensibility introduced by
$mapping was never initialized. Although not strictly required by PHP, it is generally a good practice to add $mapping = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
494
                    $mapping['class']        = $embeddedAnnot->class;
0 ignored issues
show
Bug introduced by
The variable $mapping does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
495
                    $mapping['columnPrefix'] = $embeddedAnnot->columnPrefix;
496
497 10
                    $classMetadata->mapEmbedded($mapping);
498 10
                    break;
499
500
                default:
501
                    $property = new Mapping\TransientMetadata($reflProperty->getName());
502
503
                    $classMetadata->addProperty($property);
504 357
                    break;
505
            }
506 18
        }
507 17
508 17
        // Evaluate AssociationOverrides annotation
509
        if (isset($classAnnotations[Annotation\AssociationOverrides::class])) {
510
            $associationOverridesAnnot = $classAnnotations[Annotation\AssociationOverrides::class];
511
512 357
            foreach ($associationOverridesAnnot->value as $associationOverride) {
513
                $fieldName = $associationOverride->name;
514
                $property  = $classMetadata->getProperty($fieldName);
515
516
                if (! $property) {
517
                    throw Mapping\MappingException::invalidOverrideFieldName($classMetadata->getClassName(), $fieldName);
518
                }
519
520
                $existingClass = get_class($property);
521
                $override      = new $existingClass($fieldName);
522
523
                // Check for JoinColumn/JoinColumns annotations
524 241
                if ($associationOverride->joinColumns) {
525
                    $joinColumns = [];
526 241
527
                    foreach ($associationOverride->joinColumns as $joinColumnAnnot) {
528 241
                        $joinColumns[] = $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot);
529
                    }
530
531
                    $override->setJoinColumns($joinColumns);
532 241
                }
533
534
                // Check for JoinTable annotations
535
                if ($associationOverride->joinTable) {
536
                    $joinTableAnnot    = $associationOverride->joinTable;
537
                    $joinTableMetadata = $this->convertJoinTableAnnotationToJoinTableMetadata($joinTableAnnot);
538
539
                    $override->setJoinTable($joinTableMetadata);
540
                }
541
542 27
                // Check for inversedBy
543
                if ($associationOverride->inversedBy) {
544 27
                    $override->setInversedBy($associationOverride->inversedBy);
545 27
                }
546
547 27
                // Check for fetch
548 21
                if ($associationOverride->fetch) {
549 14
                    $override->setFetchMode(
550
                        constant(Mapping\FetchMode::class . '::' . $associationOverride->fetch)
551
                    );
552 21
                }
553 10
554
                $classMetadata->setPropertyOverride($override);
555
            }
556 21
        }
557 11
558
        // Evaluate AttributeOverrides annotation
559
        if (isset($classAnnotations[Annotation\AttributeOverrides::class])) {
560 21
            $attributeOverridesAnnot = $classAnnotations[Annotation\AttributeOverrides::class];
561 7
562
            foreach ($attributeOverridesAnnot->value as $attributeOverrideAnnot) {
563
                $fieldMetadata = $this->convertColumnAnnotationToFieldMetadata(
564 21
                    $attributeOverrideAnnot->column,
565 8
                    $attributeOverrideAnnot->name,
566
                    false
567
                );
568 21
569 6
                $classMetadata->setPropertyOverride($fieldMetadata);
570
            }
571
        }
572 21
573 11
        return $classMetadata;
574
    }
575
576 21 View Code Duplication
    private function convertClassAnnotationsToClassMetadata(
577 21
        array $classAnnotations,
578
        \ReflectionClass $reflectionClass,
579
        Mapping\ClassMetadata $metadata
580
    ) : Mapping\ClassMetadata
581 27
    {
582
        switch (true) {
583
            case isset($classAnnotations[Annotation\Entity::class]):
584
                return $this->convertClassAnnotationsToEntityClassMetadata(
585
                    $classAnnotations,
586
                    $reflectionClass,
587
                    $metadata
588
                );
589
590
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
591 173
592
            case isset($classAnnotations[Annotation\MappedSuperclass::class]):
593
                return $this->convertClassAnnotationsToMappedSuperClassMetadata(
594 173
                    $classAnnotations,
595 173
                    $reflectionClass,
596 173
                    $metadata
597 173
                );
598 173
599 173
            case isset($classAnnotations[Annotation\Embeddable::class]):
600
                return $this->convertClassAnnotationsToEmbeddableClassMetadata(
601
                    $classAnnotations,
602
                    $reflectionClass,
603
                    $metadata
604
                );
605
606
            default:
607
                throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($reflectionClass->getName());
0 ignored issues
show
Bug introduced by
Consider using $reflectionClass->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
608
        }
609
    }
610
611 347
    /**
612
     * @param array                 $classAnnotations
613
     * @param \ReflectionClass      $reflectionClass
614 347
     * @param Mapping\ClassMetadata $metadata
615 347
     *
616 347
     * @return Mapping\ClassMetadata
617 347
     *
618 347
     * @throws Mapping\MappingException
619
     * @throws \UnexpectedValueException
620
     */
621 347 View Code Duplication
    private function convertClassAnnotationsToEntityClassMetadata(
622 9
        array $classAnnotations,
623
        \ReflectionClass $reflectionClass,
0 ignored issues
show
Unused Code introduced by
The parameter $reflectionClass is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
624
        Mapping\ClassMetadata $metadata
625 347
    )
626 80
    {
627
        /** @var Annotation\Entity $entityAnnot */
628
        $entityAnnot  = $classAnnotations[Annotation\Entity::class];
629 347
630 5
        if ($entityAnnot->repositoryClass !== null) {
631
            $metadata->setCustomRepositoryClassName(
632
                $metadata->fullyQualifiedClassName($entityAnnot->repositoryClass)
633 347
            );
634
        }
635
636
        if ($entityAnnot->readOnly) {
637
            $metadata->asReadOnly();
638
        }
639
640
        return $metadata;
641
    }
642
643
    /**
644
     * @param array                 $classAnnotations
645
     * @param \ReflectionClass      $reflectionClass
646
     * @param Mapping\ClassMetadata $metadata
647
     *
648
     * @return Mapping\ClassMetadata
649
     */
650
    private function convertClassAnnotationsToMappedSuperClassMetadata(
651
        array $classAnnotations,
652
        \ReflectionClass $reflectionClass,
0 ignored issues
show
Unused Code introduced by
The parameter $reflectionClass is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
653
        Mapping\ClassMetadata $metadata
654
    ) : Mapping\ClassMetadata
655
    {
656
        /** @var Annotation\MappedSuperclass $mappedSuperclassAnnot */
657
        $mappedSuperclassAnnot = $classAnnotations[Annotation\MappedSuperclass::class];
658
659
        if ($mappedSuperclassAnnot->repositoryClass !== null) {
660
            $metadata->setCustomRepositoryClassName(
661
                $metadata->fullyQualifiedClassName($mappedSuperclassAnnot->repositoryClass)
662
            );
663
        }
664
665
        $metadata->isMappedSuperclass = true;
666
        $metadata->isEmbeddedClass = false;
667
668
        return $metadata;
669
    }
670
671
    /**
672
     * @param array                 $classAnnotations
673
     * @param \ReflectionClass      $reflectionClass
674
     * @param Mapping\ClassMetadata $metadata
675
     *
676
     * @return Mapping\ClassMetadata
677
     */
678
    private function convertClassAnnotationsToEmbeddableClassMetadata(
679
        array $classAnnotations,
0 ignored issues
show
Unused Code introduced by
The parameter $classAnnotations is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
680
        \ReflectionClass $reflectionClass,
0 ignored issues
show
Unused Code introduced by
The parameter $reflectionClass is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
681
        Mapping\ClassMetadata $metadata
682
    ) : Mapping\ClassMetadata
683
    {
684
        $metadata->isMappedSuperclass = false;
685
        $metadata->isEmbeddedClass = true;
686
687
        return $metadata;
688
    }
689
690
    /**
691
     * @param Annotation\SqlResultSetMapping $resultSetMapping
692
     *
693
     * @return array
694
     */
695 View Code Duplication
    private function convertSqlResultSetMapping(Annotation\SqlResultSetMapping $resultSetMapping)
696
    {
697
        $entities = [];
698
699
        foreach ($resultSetMapping->entities as $entityResultAnnot) {
700
            $entityResult = [
701
                'fields'                => [],
702
                'entityClass'           => $entityResultAnnot->entityClass,
703
                'discriminatorColumn'   => $entityResultAnnot->discriminatorColumn,
704
            ];
705
706
            foreach ($entityResultAnnot->fields as $fieldResultAnnot) {
707
                $entityResult['fields'][] = [
708
                    'name'      => $fieldResultAnnot->name,
709
                    'column'    => $fieldResultAnnot->column
710
                ];
711
            }
712
713
            $entities[] = $entityResult;
714
        }
715
716
        $columns = [];
717
718
        foreach ($resultSetMapping->columns as $columnResultAnnot) {
719
            $columns[] = [
720
                'name' => $columnResultAnnot->name,
721
            ];
722
        }
723
724
        return [
725
            'name'     => $resultSetMapping->name,
726
            'entities' => $entities,
727
            'columns'  => $columns
728
        ];
729
    }
730
731
    private function convertReflectionPropertyToFieldMetadata(
732
        \ReflectionProperty $reflProperty,
733
        array $propertyAnnotations
734
    )
735
    {
736
        $className   = $reflProperty->getDeclaringClass()->getName();
0 ignored issues
show
introduced by
Consider using $reflProperty->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
737
        $fieldName   = $reflProperty->getName();
738
        $isVersioned = isset($propertyAnnotations[Annotation\Version::class]);
739
        $columnAnnot = $propertyAnnotations[Annotation\Column::class];
740
741
        if ($columnAnnot->type == null) {
742
            throw Mapping\MappingException::propertyTypeIsRequired($className, $fieldName);
743
        }
744
745
        $fieldMetadata = $this->convertColumnAnnotationToFieldMetadata($columnAnnot, $fieldName, $isVersioned);
746
747
        // Check for Id
748
        if (isset($propertyAnnotations[Annotation\Id::class])) {
749
            $fieldMetadata->setPrimaryKey(true);
750
        }
751
752
        // Check for GeneratedValue strategy
753
        if (isset($propertyAnnotations[Annotation\GeneratedValue::class])) {
754
            $generatedValueAnnot = $propertyAnnotations[Annotation\GeneratedValue::class];
755
            $strategy = strtoupper($generatedValueAnnot->strategy);
756
            $idGeneratorType = constant(sprintf('%s::%s', Mapping\GeneratorType::class, $strategy));
757
758
            if ($idGeneratorType !== Mapping\GeneratorType::NONE) {
759
                $idGeneratorDefinition = [];
760
761
                // Check for CustomGenerator/SequenceGenerator/TableGenerator definition
762
                switch (true) {
763
                    case isset($propertyAnnotations[Annotation\SequenceGenerator::class]):
764
                        $seqGeneratorAnnot = $propertyAnnotations[Annotation\SequenceGenerator::class];
765
766
                        $idGeneratorDefinition = [
767
                            'sequenceName' => $seqGeneratorAnnot->sequenceName,
768
                            'allocationSize' => $seqGeneratorAnnot->allocationSize,
769
                        ];
770
771
                        break;
772
773
                    case isset($propertyAnnotations[Annotation\CustomIdGenerator::class]):
774
                        $customGeneratorAnnot = $propertyAnnotations[Annotation\CustomIdGenerator::class];
775
776
                        $idGeneratorDefinition = [
777
                            'class' => $customGeneratorAnnot->class,
778
                            'arguments' => $customGeneratorAnnot->arguments,
779
                        ];
780
781
                        break;
782
783
                    /* @todo If it is not supported, why does this exist? */
784
                    case isset($propertyAnnotations['Doctrine\ORM\Mapping\TableGenerator']):
785
                        throw Mapping\MappingException::tableIdGeneratorNotImplemented($className);
786
                }
787
788
                $fieldMetadata->setValueGenerator(new Mapping\ValueGeneratorMetadata($idGeneratorType, $idGeneratorDefinition));
789
            }
790
        }
791
792
        return $fieldMetadata;
793
    }
794
795
    private function convertReflectionPropertyToOneToOneAssociationMetadata(
796
        \ReflectionProperty $reflProperty,
797
        array $propertyAnnotations,
798
        Mapping\ClassMetadata $metadata
799
    )
800
    {
801
        $className     = $reflProperty->getDeclaringClass()->getName();
0 ignored issues
show
introduced by
Consider using $reflProperty->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
802
        $fieldName     = $reflProperty->getName();
803
        $oneToOneAnnot = $propertyAnnotations[Annotation\OneToOne::class];
804
        $assocMetadata = new Mapping\OneToOneAssociationMetadata($fieldName);
805
        $targetEntity  = $metadata->fullyQualifiedClassName($oneToOneAnnot->targetEntity);
806
807
        $assocMetadata->setTargetEntity($targetEntity);
808
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToOneAnnot->cascade));
809
        $assocMetadata->setOrphanRemoval($oneToOneAnnot->orphanRemoval);
810
        $assocMetadata->setFetchMode($this->getFetchMode($className, $oneToOneAnnot->fetch));
811
812
        if (! empty($oneToOneAnnot->mappedBy)) {
813
            $assocMetadata->setMappedBy($oneToOneAnnot->mappedBy);
814
        }
815
816
        if (! empty($oneToOneAnnot->inversedBy)) {
817
            $assocMetadata->setInversedBy($oneToOneAnnot->inversedBy);
818
        }
819
820
        // Check for Id
821
        if (isset($propertyAnnotations[Annotation\Id::class])) {
822
            $assocMetadata->setPrimaryKey(true);
823
        }
824
825
        // Check for Cache
826 View Code Duplication
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
827
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
828
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $metadata, $fieldName);
829
830
            $assocMetadata->setCache($cacheMetadata);
831
        }
832
833
        // Check for JoinColumn/JoinColumns annotations
834 View Code Duplication
        switch (true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
835
            case isset($propertyAnnotations[Annotation\JoinColumn::class]):
836
                $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class];
837
838
                $assocMetadata->addJoinColumn(
839
                    $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot)
840
                );
841
842
                break;
843
844
            case isset($propertyAnnotations[Annotation\JoinColumns::class]):
845
                $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class];
846
847
                foreach ($joinColumnsAnnot->value as $joinColumnAnnot) {
848
                    $assocMetadata->addJoinColumn(
849
                        $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot)
850
                    );
851
                }
852
853
                break;
854
        }
855
856
        return $assocMetadata;
857
    }
858
859
    private function convertReflectionPropertyToManyToOneAssociationMetadata(
860
        \ReflectionProperty $reflProperty,
861
        array $propertyAnnotations,
862
        Mapping\ClassMetadata $metadata
863
    )
864
    {
865
        $className      = $reflProperty->getDeclaringClass()->getName();
0 ignored issues
show
introduced by
Consider using $reflProperty->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
866
        $fieldName      = $reflProperty->getName();
867
        $manyToOneAnnot = $propertyAnnotations[Annotation\ManyToOne::class];
868
        $assocMetadata  = new Mapping\ManyToOneAssociationMetadata($fieldName);
869
        $targetEntity  = $metadata->fullyQualifiedClassName($manyToOneAnnot->targetEntity);
870
871
        $assocMetadata->setTargetEntity($targetEntity);
872
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToOneAnnot->cascade));
873
        $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToOneAnnot->fetch));
874
875
        if (! empty($manyToOneAnnot->inversedBy)) {
876
            $assocMetadata->setInversedBy($manyToOneAnnot->inversedBy);
877
        }
878
879
        // Check for Id
880
        if (isset($propertyAnnotations[Annotation\Id::class])) {
881
            $assocMetadata->setPrimaryKey(true);
882
        }
883
884
        // Check for Cache
885 View Code Duplication
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
886
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
887
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $metadata, $fieldName);
888
889
            $assocMetadata->setCache($cacheMetadata);
890
        }
891
892
        // Check for JoinColumn/JoinColumns annotations
893 View Code Duplication
        switch (true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
894
            case isset($propertyAnnotations[Annotation\JoinColumn::class]):
895
                $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class];
896
897
                $assocMetadata->addJoinColumn(
898
                    $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot)
899
                );
900
901
                break;
902
903
            case isset($propertyAnnotations[Annotation\JoinColumns::class]):
904
                $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class];
905
906
                foreach ($joinColumnsAnnot->value as $joinColumnAnnot) {
907
                    $assocMetadata->addJoinColumn(
908
                        $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot)
909
                    );
910
                }
911
912
                break;
913
        }
914
915
        return $assocMetadata;
916
    }
917
918
    private function convertReflectionPropertyToOneToManyAssociationMetadata(
919
        \ReflectionProperty $reflProperty,
920
        array $propertyAnnotations,
921
        Mapping\ClassMetadata $metadata
922
    )
923
    {
924
        $className      = $reflProperty->getDeclaringClass()->getName();
0 ignored issues
show
introduced by
Consider using $reflProperty->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
925
        $fieldName      = $reflProperty->getName();
926
        $oneToManyAnnot = $propertyAnnotations[Annotation\OneToMany::class];
927
        $assocMetadata  = new Mapping\OneToManyAssociationMetadata($fieldName);
928
        $targetEntity  = $metadata->fullyQualifiedClassName($oneToManyAnnot->targetEntity);
929
930
        $assocMetadata->setTargetEntity($targetEntity);
931
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToManyAnnot->cascade));
932
        $assocMetadata->setOrphanRemoval($oneToManyAnnot->orphanRemoval);
933
        $assocMetadata->setFetchMode($this->getFetchMode($className, $oneToManyAnnot->fetch));
934
935
        if (! empty($oneToManyAnnot->mappedBy)) {
936
            $assocMetadata->setMappedBy($oneToManyAnnot->mappedBy);
937
        }
938
939
        if (! empty($oneToManyAnnot->indexBy)) {
940
            $assocMetadata->setIndexedBy($oneToManyAnnot->indexBy);
941
        }
942
943
        // Check for OrderBy
944 View Code Duplication
        if (isset($propertyAnnotations[Annotation\OrderBy::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
945
            $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class];
946
947
            $assocMetadata->setOrderBy($orderByAnnot->value);
948
        }
949
950
        // Check for Id
951
        if (isset($propertyAnnotations[Annotation\Id::class])) {
952
            $assocMetadata->setPrimaryKey(true);
953
        }
954
955
        // Check for Cache
956 View Code Duplication
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
957
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
958
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $metadata, $fieldName);
959
960
            $assocMetadata->setCache($cacheMetadata);
961
        }
962
963
        return $assocMetadata;
964
    }
965
966
    private function convertReflectionPropertyToManyToManyAssociationMetadata(
967
        \ReflectionProperty $reflProperty,
968
        array $propertyAnnotations,
969
        Mapping\ClassMetadata $metadata
970
    )
971
    {
972
        $className       = $reflProperty->getDeclaringClass()->getName();
0 ignored issues
show
introduced by
Consider using $reflProperty->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
973
        $fieldName       = $reflProperty->getName();
974
        $manyToManyAnnot = $propertyAnnotations[Annotation\ManyToMany::class];
975
        $assocMetadata   = new Mapping\ManyToManyAssociationMetadata($fieldName);
976
        $targetEntity    = $metadata->fullyQualifiedClassName($manyToManyAnnot->targetEntity);
977
978
        $assocMetadata->setTargetEntity($targetEntity);
979
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToManyAnnot->cascade));
980
        $assocMetadata->setOrphanRemoval($manyToManyAnnot->orphanRemoval);
981
        $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToManyAnnot->fetch));
982
983
        if (! empty($manyToManyAnnot->mappedBy)) {
984
            $assocMetadata->setMappedBy($manyToManyAnnot->mappedBy);
985
        }
986
987
        if (! empty($manyToManyAnnot->inversedBy)) {
988
            $assocMetadata->setInversedBy($manyToManyAnnot->inversedBy);
989
        }
990
991
        if (! empty($manyToManyAnnot->indexBy)) {
992
            $assocMetadata->setIndexedBy($manyToManyAnnot->indexBy);
993
        }
994
995
        // Check for JoinTable
996 View Code Duplication
        if (isset($propertyAnnotations[Annotation\JoinTable::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
997
            $joinTableAnnot    = $propertyAnnotations[Annotation\JoinTable::class];
998
            $joinTableMetadata = $this->convertJoinTableAnnotationToJoinTableMetadata($joinTableAnnot);
999
1000
            $assocMetadata->setJoinTable($joinTableMetadata);
1001
        }
1002
1003
        // Check for OrderBy
1004 View Code Duplication
        if (isset($propertyAnnotations[Annotation\OrderBy::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1005
            $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class];
1006
1007
            $assocMetadata->setOrderBy($orderByAnnot->value);
1008
        }
1009
1010
        // Check for Id
1011
        if (isset($propertyAnnotations[Annotation\Id::class])) {
1012
            $assocMetadata->setPrimaryKey(true);
1013
        }
1014
1015
        // Check for Cache
1016 View Code Duplication
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1017
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
1018
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $metadata, $fieldName);
1019
1020
            $assocMetadata->setCache($cacheMetadata);
1021
        }
1022
1023
        return $assocMetadata;
1024
    }
1025
1026
    /**
1027
     * Parse the given Column as FieldMetadata
1028
     *
1029
     * @param Annotation\Column $columnAnnot
1030
     * @param string            $fieldName
1031
     * @param bool              $isVersioned
1032
     *
1033
     * @return Mapping\FieldMetadata
1034
     */
1035
    private function convertColumnAnnotationToFieldMetadata(
1036
        Annotation\Column $columnAnnot,
1037
        string $fieldName,
1038
        bool $isVersioned
1039
    ) : Mapping\FieldMetadata
1040
    {
1041
        $fieldMetadata = $isVersioned
1042
            ? new Mapping\VersionFieldMetadata($fieldName)
1043
            : new Mapping\FieldMetadata($fieldName)
1044
        ;
1045
1046
        $fieldMetadata->setType(Type::getType($columnAnnot->type));
1047
1048
        if (! empty($columnAnnot->name)) {
1049
            $fieldMetadata->setColumnName($columnAnnot->name);
1050
        }
1051
1052
        if (! empty($columnAnnot->columnDefinition)) {
1053
            $fieldMetadata->setColumnDefinition($columnAnnot->columnDefinition);
1054
        }
1055
1056
        if (! empty($columnAnnot->length)) {
1057
            $fieldMetadata->setLength($columnAnnot->length);
1058
        }
1059
1060
        if ($columnAnnot->options) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $columnAnnot->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...
1061
            $fieldMetadata->setOptions($columnAnnot->options);
1062
        }
1063
1064
        $fieldMetadata->setScale($columnAnnot->scale);
1065
        $fieldMetadata->setPrecision($columnAnnot->precision);
1066
        $fieldMetadata->setNullable($columnAnnot->nullable);
1067
        $fieldMetadata->setUnique($columnAnnot->unique);
1068
1069
        return $fieldMetadata;
1070
    }
1071
1072
    /**
1073
     * Parse the given Table as TableMetadata
1074
     *
1075
     * @param Annotation\Table $tableAnnot
1076
     *
1077
     * @return Mapping\TableMetadata
1078
     */
1079 View Code Duplication
    private function convertTableAnnotationToTableMetadata(
1080
        Annotation\Table $tableAnnot
1081
    ) : Mapping\TableMetadata
1082
    {
1083
        $table = new Mapping\TableMetadata();
1084
1085
        if (! empty($tableAnnot->name)) {
1086
            $table->setName($tableAnnot->name);
1087
        }
1088
1089
        if (! empty($tableAnnot->schema)) {
1090
            $table->setSchema($tableAnnot->schema);
1091
        }
1092
1093
        foreach ($tableAnnot->options as $optionName => $optionValue) {
1094
            $table->addOption($optionName, $optionValue);
1095
        }
1096
1097
        foreach ($tableAnnot->indexes as $indexAnnot) {
1098
            $table->addIndex([
1099
                'name'    => $indexAnnot->name,
1100
                'columns' => $indexAnnot->columns,
1101
                'unique'  => $indexAnnot->unique,
1102
                'options' => $indexAnnot->options,
1103
                'flags'   => $indexAnnot->flags,
1104
            ]);
1105
        }
1106
1107
        foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) {
1108
            $table->addUniqueConstraint([
1109
                'name'    => $uniqueConstraintAnnot->name,
1110
                'columns' => $uniqueConstraintAnnot->columns,
1111
                'options' => $uniqueConstraintAnnot->options,
1112
                'flags'   => $uniqueConstraintAnnot->flags,
1113
            ]);
1114
        }
1115
1116
        return $table;
1117
    }
1118
1119
    /**
1120
     * Parse the given JoinTable as JoinTableMetadata
1121
     *
1122
     * @param Annotation\JoinTable $joinTableAnnot
1123
     *
1124
     * @return Mapping\JoinTableMetadata
1125
     */
1126
    private function convertJoinTableAnnotationToJoinTableMetadata(
1127
        Annotation\JoinTable $joinTableAnnot
1128
    ) : Mapping\JoinTableMetadata
1129
    {
1130
        $joinTable = new Mapping\JoinTableMetadata();
1131
1132
        if (! empty($joinTableAnnot->name)) {
1133
            $joinTable->setName($joinTableAnnot->name);
1134
        }
1135
1136
        if (! empty($joinTableAnnot->schema)) {
1137
            $joinTable->setSchema($joinTableAnnot->schema);
1138
        }
1139
1140
        foreach ($joinTableAnnot->joinColumns as $joinColumnAnnot) {
1141
            $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot);
1142
1143
            $joinTable->addJoinColumn($joinColumn);
1144
        }
1145
1146
        foreach ($joinTableAnnot->inverseJoinColumns as $joinColumnAnnot) {
1147
            $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot);
1148
1149
            $joinTable->addInverseJoinColumn($joinColumn);
1150
        }
1151
1152
        return $joinTable;
1153
    }
1154
1155
    /**
1156
     * Parse the given JoinColumn as JoinColumnMetadata
1157
     *
1158
     * @param Annotation\JoinColumn $joinColumnAnnot
1159
     *
1160
     * @return Mapping\JoinColumnMetadata
1161
     */
1162
    private function convertJoinColumnAnnotationToJoinColumnMetadata(
1163
        Annotation\JoinColumn $joinColumnAnnot
1164
    ) : Mapping\JoinColumnMetadata
1165
    {
1166
        $joinColumn = new Mapping\JoinColumnMetadata();
1167
1168
        // @todo Remove conditionals for name and referencedColumnName once naming strategy is brought into drivers
1169
        if (! empty($joinColumnAnnot->name)) {
1170
            $joinColumn->setColumnName($joinColumnAnnot->name);
1171
        }
1172
1173
        if (! empty($joinColumnAnnot->referencedColumnName)) {
1174
            $joinColumn->setReferencedColumnName($joinColumnAnnot->referencedColumnName);
1175
        }
1176
1177
        $joinColumn->setNullable($joinColumnAnnot->nullable);
1178
        $joinColumn->setUnique($joinColumnAnnot->unique);
1179
1180
        if (! empty($joinColumnAnnot->fieldName)) {
1181
            $joinColumn->setAliasedName($joinColumnAnnot->fieldName);
1182
        }
1183
1184
        if (! empty($joinColumnAnnot->columnDefinition)) {
1185
            $joinColumn->setColumnDefinition($joinColumnAnnot->columnDefinition);
1186
        }
1187
1188
        if ($joinColumnAnnot->onDelete) {
1189
            $joinColumn->setOnDelete(strtoupper($joinColumnAnnot->onDelete));
1190
        }
1191
1192
        return $joinColumn;
1193
    }
1194
1195
    /**
1196
     * Parse the given Cache as CacheMetadata
1197
     *
1198
     * @param Annotation\Cache      $cacheAnnot
1199
     * @param Mapping\ClassMetadata $metadata
1200
     * @param null|string           $fieldName
1201
     *
1202
     * @return Mapping\CacheMetadata
1203
     */
1204 View Code Duplication
    private function convertCacheAnnotationToCacheMetadata(
1205
        Annotation\Cache $cacheAnnot,
1206
        Mapping\ClassMetadata $metadata,
1207
        $fieldName = null
1208
    ) : Mapping\CacheMetadata
1209
    {
1210
        $baseRegion    = strtolower(str_replace('\\', '_', $metadata->getRootClassName()));
1211
        $defaultRegion = $baseRegion . ($fieldName ? '__' . $fieldName : '');
1212
1213
        $usage = constant(sprintf('%s::%s', Mapping\CacheUsage::class, $cacheAnnot->usage));
1214
        $region = $cacheAnnot->region ?: $defaultRegion;
1215
1216
        return new Mapping\CacheMetadata($usage, $region);
1217
    }
1218
1219
    /**
1220
     * Attempts to resolve the cascade modes.
1221
     *
1222
     * @param string $className        The class name.
1223
     * @param string $fieldName        The field name.
1224
     * @param array  $originalCascades The original unprocessed field cascades.
1225
     *
1226
     * @return array The processed field cascades.
1227
     *
1228
     * @throws Mapping\MappingException If a cascade option is not valid.
1229
     */
1230 View Code Duplication
    private function getCascade(string $className, string $fieldName, array $originalCascades)
1231
    {
1232
        $cascadeTypes = ['remove', 'persist', 'refresh', 'merge', 'detach'];
1233
        $cascades     = array_map('strtolower', $originalCascades);
1234
1235
        if (in_array('all', $cascades)) {
1236
            $cascades = $cascadeTypes;
1237
        }
1238
1239
        if (count($cascades) !== count(array_intersect($cascades, $cascadeTypes))) {
1240
            $diffCascades = array_diff($cascades, array_intersect($cascades, $cascadeTypes));
1241
1242
            throw Mapping\MappingException::invalidCascadeOption($diffCascades, $className, $fieldName);
1243
        }
1244
1245
        return $cascades;
1246
    }
1247
1248
    /**
1249
     * Attempts to resolve the fetch mode.
1250
     *
1251
     * @param string $className The class name.
1252
     * @param string $fetchMode The fetch mode.
1253
     *
1254
     * @return string The fetch mode as defined in ClassMetadata.
1255
     *
1256
     * @throws Mapping\MappingException If the fetch mode is not valid.
1257
     */
1258 View Code Duplication
    private function getFetchMode($className, $fetchMode) : string
1259
    {
1260
        $fetchModeConstant = sprintf('%s::%s', Mapping\FetchMode::class, $fetchMode);
1261
1262
        if ( ! defined($fetchModeConstant)) {
1263
            throw Mapping\MappingException::invalidFetchMode($className, $fetchMode);
1264
        }
1265
1266
        return constant($fetchModeConstant);
1267
    }
1268
1269
    /**
1270
     * Parses the given method.
1271
     *
1272
     * @param \ReflectionMethod $method
1273
     *
1274
     * @return array
1275
     */
1276 View Code Duplication
    private function getMethodCallbacks(\ReflectionMethod $method) : array
1277
    {
1278
        $annotations = $this->getMethodAnnotations($method);
1279
        $events      = [
1280
            Events::prePersist  => Annotation\PrePersist::class,
1281
            Events::postPersist => Annotation\PostPersist::class,
1282
            Events::preUpdate   => Annotation\PreUpdate::class,
1283
            Events::postUpdate  => Annotation\PostUpdate::class,
1284
            Events::preRemove   => Annotation\PreRemove::class,
1285
            Events::postRemove  => Annotation\PostRemove::class,
1286
            Events::postLoad    => Annotation\PostLoad::class,
1287
            Events::preFlush    => Annotation\PreFlush::class,
1288
        ];
1289
1290
        // Check for callbacks
1291
        $callbacks = [];
1292
1293
        foreach ($events as $eventName => $annotationClassName) {
1294
            if (isset($annotations[$annotationClassName]) || $method->getName() === $eventName) {
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
1295
                $callbacks[] = $eventName;
1296
            }
1297
        }
1298
1299
        return $callbacks;
1300
    }
1301
1302
    /**
1303
     * @param \ReflectionClass $reflectionClass
1304
     *
1305
     * @return array
1306
     */
1307 View Code Duplication
    private function getClassAnnotations(\ReflectionClass $reflectionClass) : array
1308
    {
1309
        $classAnnotations = $this->reader->getClassAnnotations($reflectionClass);
1310
1311
        foreach ($classAnnotations as $key => $annot) {
1312
            if (! is_numeric($key)) {
1313
                continue;
1314
            }
1315
1316
            $classAnnotations[get_class($annot)] = $annot;
1317
        }
1318
1319
        return $classAnnotations;
1320
    }
1321
1322
    /**
1323
     * @param \ReflectionProperty $reflectionProperty
1324
     *
1325
     * @return array
1326
     */
1327 View Code Duplication
    private function getPropertyAnnotations(\ReflectionProperty $reflectionProperty) : array
1328
    {
1329
        $propertyAnnotations = $this->reader->getPropertyAnnotations($reflectionProperty);
1330
1331
        foreach ($propertyAnnotations as $key => $annot) {
1332
            if (! is_numeric($key)) {
1333
                continue;
1334
            }
1335
1336
            $propertyAnnotations[get_class($annot)] = $annot;
1337
        }
1338
1339
        return $propertyAnnotations;
1340
    }
1341
1342
    /**
1343
     * @param \ReflectionMethod $reflectionMethod
1344
     *
1345
     * @return array
1346
     */
1347 View Code Duplication
    private function getMethodAnnotations(\ReflectionMethod $reflectionMethod) : array
1348
    {
1349
        $methodAnnotations = $this->reader->getMethodAnnotations($reflectionMethod);
1350
1351
        foreach ($methodAnnotations as $key => $annot) {
1352
            if (! is_numeric($key)) {
1353
                continue;
1354
            }
1355
1356
            $methodAnnotations[get_class($annot)] = $annot;
1357
        }
1358
1359
        return $methodAnnotations;
1360
    }
1361
1362
    /**
1363
     * Factory method for the Annotation Driver.
1364
     *
1365
     * @param array|string          $paths
1366
     * @param AnnotationReader|null $reader
1367
     *
1368
     * @return AnnotationDriver
1369
     */
1370
    static public function create($paths = [], AnnotationReader $reader = null)
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
1371
    {
1372
        if ($reader == null) {
1373
            $reader = new AnnotationReader();
1374
        }
1375
1376
        return new self($reader, $paths);
1377
    }
1378
}
1379