Completed
Pull Request — master (#1803)
by Andreas
16:13
created

AnnotationDriver::loadMetadataForClass()   F

Complexity

Conditions 63
Paths > 20000

Size

Total Lines 189

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 115
CRAP Score 63.4843

Importance

Changes 0
Metric Value
dl 0
loc 189
ccs 115
cts 121
cp 0.9504
rs 0
c 0
b 0
f 0
cc 63
nc 28339242
nop 2
crap 63.4843

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB\Mapping\Driver;
6
7
use Doctrine\Common\Annotations\AnnotationReader;
8
use Doctrine\Common\Annotations\Reader;
9
use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver;
10
use Doctrine\ODM\MongoDB\Events;
11
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
12
use Doctrine\ODM\MongoDB\Mapping\Annotations\AbstractIndex;
13
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
14
use Doctrine\ODM\MongoDB\Mapping\MappingException;
15
use ReflectionClass;
16
use ReflectionMethod;
17
use const E_USER_DEPRECATED;
18
use function array_merge;
19
use function array_replace;
20
use function assert;
21
use function constant;
22
use function get_class;
23
use function is_array;
24
use function trigger_error;
25
26
/**
27
 * The AnnotationDriver reads the mapping metadata from docblock annotations.
28
 */
29
class AnnotationDriver extends AbstractAnnotationDriver
30
{
31 918
    public function isTransient($className)
32
    {
33 918
        $classAnnotations = $this->reader->getClassAnnotations(new ReflectionClass($className));
34
35 918
        foreach ($classAnnotations as $annot) {
36 917
            if ($annot instanceof ODM\AbstractDocument) {
37 917
                return false;
38
            }
39
        }
40 69
        return true;
41
    }
42
43
    /**
44
     * {@inheritdoc}
45
     */
46 1441
    public function loadMetadataForClass($className, \Doctrine\Common\Persistence\Mapping\ClassMetadata $class) : void
47
    {
48 1441
        assert($class instanceof ClassMetadata);
49 1441
        $reflClass = $class->getReflectionClass();
50
51 1441
        $classAnnotations = $this->reader->getClassAnnotations($reflClass);
52
53 1441
        $documentAnnot = null;
54 1441
        foreach ($classAnnotations as $annot) {
55 1439
            $classAnnotations[get_class($annot)] = $annot;
56
57 1439
            if ($annot instanceof ODM\AbstractDocument) {
58 1439
                if ($documentAnnot !== null) {
59 5
                    throw MappingException::classCanOnlyBeMappedByOneAbstractDocument($className, $documentAnnot, $annot);
60
                }
61 1439
                $documentAnnot = $annot;
62
            }
63
64
            // non-document class annotations
65 1439
            if ($annot instanceof ODM\AbstractIndex) {
66 6
                $this->addIndex($class, $annot);
67
            }
68 1439
            if ($annot instanceof ODM\Indexes) {
69
                // Setting the type to mixed is a workaround until https://github.com/doctrine/annotations/pull/209 is released.
70
                /** @var mixed $value */
71 94
                $value = $annot->value;
72 94
                foreach (is_array($value) ? $value : [$value] as $index) {
73 94
                    $this->addIndex($class, $index);
74
                }
75 1439
            } elseif ($annot instanceof ODM\InheritanceType) {
76 859
                $class->setInheritanceType(constant(ClassMetadata::class . '::INHERITANCE_TYPE_' . $annot->value));
77 1439
            } elseif ($annot instanceof ODM\DiscriminatorField) {
78 132
                $class->setDiscriminatorField($annot->value);
79 1439
            } elseif ($annot instanceof ODM\DiscriminatorMap) {
80
                /** @var array $value */
81 131
                $value = $annot->value;
82 131
                $class->setDiscriminatorMap($value);
83 1439
            } elseif ($annot instanceof ODM\DiscriminatorValue) {
84
                $class->setDiscriminatorValue($annot->value);
85 1439
            } elseif ($annot instanceof ODM\ChangeTrackingPolicy) {
86 66
                $class->setChangeTrackingPolicy(constant(ClassMetadata::class . '::CHANGETRACKING_' . $annot->value));
87 1439
            } elseif ($annot instanceof ODM\DefaultDiscriminatorValue) {
88 68
                $class->setDefaultDiscriminatorValue($annot->value);
89 1439
            } elseif ($annot instanceof ODM\ReadPreference) {
90 1439
                $class->setReadPreference($annot->value, $annot->tags ?? []);
91
            }
92
        }
93
94 1436
        if ($documentAnnot === null) {
95 3
            throw MappingException::classIsNotAValidDocument($className);
96
        }
97
98 1434
        if ($documentAnnot instanceof ODM\MappedSuperclass) {
99 849
            $class->isMappedSuperclass = true;
100 1433
        } elseif ($documentAnnot instanceof ODM\EmbeddedDocument) {
101 280
            $class->isEmbeddedDocument = true;
102 1426
        } elseif ($documentAnnot instanceof ODM\QueryResultDocument) {
103 67
            $class->isQueryResultDocument = true;
104 1426
        } elseif ($documentAnnot instanceof ODM\File) {
105 79
            $class->isFile = true;
106
107 79
            if ($documentAnnot->chunkSizeBytes !== null) {
108 78
                $class->setChunkSizeBytes($documentAnnot->chunkSizeBytes);
109
            }
110
        }
111
112 1434
        if (isset($documentAnnot->db)) {
113 1
            $class->setDatabase($documentAnnot->db);
114
        }
115 1434
        if (isset($documentAnnot->collection)) {
116 904
            $class->setCollection($documentAnnot->collection);
117
        }
118
        // Store bucketName as collection name for GridFS files
119 1434
        if (isset($documentAnnot->bucketName)) {
120 1
            $class->setBucketName($documentAnnot->bucketName);
121
        }
122 1434
        if (isset($documentAnnot->repositoryClass)) {
123 77
            $class->setCustomRepositoryClass($documentAnnot->repositoryClass);
124
        }
125 1434
        if (isset($documentAnnot->writeConcern)) {
126 10
            $class->setWriteConcern($documentAnnot->writeConcern);
127
        }
128 1434
        if (isset($documentAnnot->indexes)) {
129 1433
            foreach ($documentAnnot->indexes as $index) {
130
                $this->addIndex($class, $index);
131
            }
132
        }
133 1434
        if (! empty($documentAnnot->readOnly)) {
134 5
            $class->markReadOnly();
135
        }
136
137 1434
        foreach ($reflClass->getProperties() as $property) {
138 1433
            if (($class->isMappedSuperclass && ! $property->isPrivate())
139
                ||
140 1433
                ($class->isInheritedField($property->name) && $property->getDeclaringClass()->name !== $class->name)) {
141 891
                continue;
142
            }
143
144 1432
            $indexes    = [];
145 1432
            $mapping    = ['fieldName' => $property->getName()];
146 1432
            $fieldAnnot = null;
147
148 1432
            foreach ($this->reader->getPropertyAnnotations($property) as $annot) {
149 1432
                if ($annot instanceof ODM\AbstractField) {
150 1432
                    $fieldAnnot = $annot;
151 1432
                    if ($annot->isDeprecated()) {
152
                        @trigger_error($annot->getDeprecationMessage(), E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
153
                    }
154
                }
155 1432
                if ($annot instanceof ODM\AbstractIndex) {
156 182
                    $indexes[] = $annot;
157
                }
158 1432
                if ($annot instanceof ODM\Indexes) {
159
                    // Setting the type to mixed is a workaround until https://github.com/doctrine/annotations/pull/209 is released.
160
                    /** @var mixed $value */
161
                    $value = $annot->value;
162
                    foreach (is_array($value) ? $value : [$value] as $index) {
163
                        $indexes[] = $index;
164
                    }
165 1432
                } elseif ($annot instanceof ODM\AlsoLoad) {
166 15
                    $mapping['alsoLoadFields'] = (array) $annot->value;
167 1432
                } elseif ($annot instanceof ODM\Version) {
168 108
                    $mapping['version'] = true;
169 1432
                } elseif ($annot instanceof ODM\Lock) {
170 1432
                    $mapping['lock'] = true;
171
                }
172
            }
173
174 1432
            if ($fieldAnnot) {
175 1432
                $mapping = array_replace($mapping, (array) $fieldAnnot);
176 1432
                $class->mapField($mapping);
177
            }
178
179 1432
            if (! $indexes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $indexes 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...
180 1432
                continue;
181
            }
182
183 182
            foreach ($indexes as $index) {
184 182
                $name = $mapping['name'] ?? $mapping['fieldName'];
185 182
                $keys = [$name => $index->order ?: 'asc'];
186 182
                $this->addIndex($class, $index, $keys);
187
            }
188
        }
189
190
        // Set shard key after all fields to ensure we mapped all its keys
191 1431
        if (isset($classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey'])) {
192 88
            $this->setShardKey($class, $classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey']);
193
        }
194
195
        /** @var ReflectionMethod $method */
196 1430
        foreach ($reflClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
197
            /* Filter for the declaring class only. Callbacks from parent
198
             * classes will already be registered.
199
             */
200 1178
            if ($method->getDeclaringClass()->name !== $reflClass->name) {
201 856
                continue;
202
            }
203
204 1178
            foreach ($this->reader->getMethodAnnotations($method) as $annot) {
205 850
                if ($annot instanceof ODM\AlsoLoad) {
206 13
                    $class->registerAlsoLoadMethod($method->getName(), $annot->value);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
207
                }
208
209 850
                if (! isset($classAnnotations[ODM\HasLifecycleCallbacks::class])) {
210 84
                    continue;
211
                }
212
213 831
                if ($annot instanceof ODM\PrePersist) {
214 812
                    $class->addLifecycleCallback($method->getName(), Events::prePersist);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
215 95
                } elseif ($annot instanceof ODM\PostPersist) {
216 10
                    $class->addLifecycleCallback($method->getName(), Events::postPersist);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
217 95
                } elseif ($annot instanceof ODM\PreUpdate) {
218 15
                    $class->addLifecycleCallback($method->getName(), Events::preUpdate);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
219 90
                } elseif ($annot instanceof ODM\PostUpdate) {
220 79
                    $class->addLifecycleCallback($method->getName(), Events::postUpdate);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
221 84
                } elseif ($annot instanceof ODM\PreRemove) {
222 82
                    $class->addLifecycleCallback($method->getName(), Events::preRemove);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
223 84
                } elseif ($annot instanceof ODM\PostRemove) {
224 82
                    $class->addLifecycleCallback($method->getName(), Events::postRemove);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
225 84
                } elseif ($annot instanceof ODM\PreLoad) {
226 83
                    $class->addLifecycleCallback($method->getName(), Events::preLoad);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
227 83
                } elseif ($annot instanceof ODM\PostLoad) {
228 82
                    $class->addLifecycleCallback($method->getName(), Events::postLoad);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
229 11
                } elseif ($annot instanceof ODM\PreFlush) {
230 1178
                    $class->addLifecycleCallback($method->getName(), Events::preFlush);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
231
                }
232
            }
233
        }
234 1430
    }
235
236 214
    private function addIndex(ClassMetadata $class, AbstractIndex $index, array $keys = []) : void
237
    {
238 214
        $keys    = array_merge($keys, $index->keys);
239 214
        $options = [];
240 214
        $allowed = ['name', 'dropDups', 'background', 'unique', 'sparse', 'expireAfterSeconds'];
241 214
        foreach ($allowed as $name) {
242 214
            if (! isset($index->$name)) {
243 214
                continue;
244
            }
245
246 214
            $options[$name] = $index->$name;
247
        }
248 214
        if (! empty($index->partialFilterExpression)) {
249 2
            $options['partialFilterExpression'] = $index->partialFilterExpression;
250
        }
251 214
        $options = array_merge($options, $index->options);
252 214
        $class->addIndex($keys, $options);
253 214
    }
254
255
    /**
256
     * @throws MappingException
257
     */
258 88
    private function setShardKey(ClassMetadata $class, ODM\ShardKey $shardKey) : void
259
    {
260 88
        $options = [];
261 88
        $allowed = ['unique', 'numInitialChunks'];
262 88
        foreach ($allowed as $name) {
263 88
            if (! isset($shardKey->$name)) {
264 88
                continue;
265
            }
266
267
            $options[$name] = $shardKey->$name;
268
        }
269
270 88
        $class->setShardKey($shardKey->keys, $options);
271 87
    }
272
273
    /**
274
     * Factory method for the Annotation Driver
275
     *
276
     * @param string[]|string $paths
277
     */
278 1704
    public static function create($paths = [], ?Reader $reader = null) : AnnotationDriver
279
    {
280 1704
        if ($reader === null) {
281 1704
            $reader = new AnnotationReader();
282
        }
283 1704
        return new self($reader, $paths);
284
    }
285
}
286