Completed
Pull Request — master (#1790)
by Andreas
17:11 queued 06:37
created

AnnotationDriver::loadMetadataForClass()   F

Complexity

Conditions 62
Paths > 20000

Size

Total Lines 181
Code Lines 117

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 112
CRAP Score 62.2992

Importance

Changes 0
Metric Value
dl 0
loc 181
ccs 112
cts 117
cp 0.9573
rs 2
c 0
b 0
f 0
cc 62
eloc 117
nc 24192042
nop 2
crap 62.2992

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\ClassMetadata;
13
use Doctrine\ODM\MongoDB\Mapping\MappingException;
14
use const E_USER_DEPRECATED;
15
use function array_merge;
16
use function array_replace;
17
use function constant;
18
use function get_class;
19
use function is_array;
20
use function ksort;
21
use function reset;
22
use function trigger_error;
23
24
/**
25
 * The AnnotationDriver reads the mapping metadata from docblock annotations.
26
 *
27
 */
28
class AnnotationDriver extends AbstractAnnotationDriver
29
{
30
    /** @var int[] */
31
    protected $entityAnnotationClasses = [
32
        ODM\Document::class            => 1,
33
        ODM\MappedSuperclass::class    => 2,
34
        ODM\EmbeddedDocument::class    => 3,
35
        ODM\QueryResultDocument::class => 4,
36
        ODM\File::class                => 5,
37
    ];
38
39
    /**
40
     * {@inheritdoc}
41
     */
42 1383
    public function loadMetadataForClass($className, \Doctrine\Common\Persistence\Mapping\ClassMetadata $class)
43
    {
44
        /** @var ClassMetadata $class */
45 1383
        $reflClass = $class->getReflectionClass();
46
47 1383
        $classAnnotations = $this->reader->getClassAnnotations($reflClass);
48
49 1383
        $documentAnnots = [];
50 1383
        foreach ($classAnnotations as $annot) {
51 1381
            $classAnnotations[get_class($annot)] = $annot;
52
53 1381
            foreach ($this->entityAnnotationClasses as $annotClass => $i) {
54 1381
                if ($annot instanceof $annotClass) {
55 1381
                    $documentAnnots[$i] = $annot;
56 1381
                    continue 2;
57
                }
58
            }
59
60
            // non-document class annotations
61 915
            if ($annot instanceof ODM\AbstractIndex) {
62 6
                $this->addIndex($class, $annot);
63
            }
64 915
            if ($annot instanceof ODM\Indexes) {
65 64
                foreach (is_array($annot->value) ? $annot->value : [$annot->value] as $index) {
66 64
                    $this->addIndex($class, $index);
67
                }
68 900
            } elseif ($annot instanceof ODM\InheritanceType) {
69 839
                $class->setInheritanceType(constant(ClassMetadata::class . '::INHERITANCE_TYPE_' . $annot->value));
70 898
            } elseif ($annot instanceof ODM\DiscriminatorField) {
71 114
                $class->setDiscriminatorField($annot->value);
72 897
            } elseif ($annot instanceof ODM\DiscriminatorMap) {
73 114
                $class->setDiscriminatorMap($annot->value);
74 836
            } elseif ($annot instanceof ODM\DiscriminatorValue) {
75
                $class->setDiscriminatorValue($annot->value);
76 836
            } elseif ($annot instanceof ODM\ChangeTrackingPolicy) {
77 49
                $class->setChangeTrackingPolicy(constant(ClassMetadata::class . '::CHANGETRACKING_' . $annot->value));
78 834
            } elseif ($annot instanceof ODM\DefaultDiscriminatorValue) {
79 51
                $class->setDefaultDiscriminatorValue($annot->value);
80 829
            } elseif ($annot instanceof ODM\ReadPreference) {
81 915
                $class->setReadPreference($annot->value, $annot->tags);
82
            }
83
        }
84
85 1383
        if (! $documentAnnots) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $documentAnnots 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...
86 3
            throw MappingException::classIsNotAValidDocument($className);
87
        }
88
89
        // find the winning document annotation
90 1381
        ksort($documentAnnots);
91 1381
        $documentAnnot = reset($documentAnnots);
92
93 1381
        if ($documentAnnot instanceof ODM\MappedSuperclass) {
94 829
            $class->isMappedSuperclass = true;
95 1380
        } elseif ($documentAnnot instanceof ODM\EmbeddedDocument) {
96 242
            $class->isEmbeddedDocument = true;
97 1373
        } elseif ($documentAnnot instanceof ODM\QueryResultDocument) {
98 50
            $class->isQueryResultDocument = true;
99 1373
        } elseif ($documentAnnot instanceof ODM\File) {
100 58
            $class->isFile = true;
101
        }
102
103 1381
        if (isset($documentAnnot->db)) {
104 1
            $class->setDatabase($documentAnnot->db);
105
        }
106 1381
        if (isset($documentAnnot->collection)) {
107 875
            $class->setCollection($documentAnnot->collection);
108
        }
109
        // Store bucketName as collection name for GridFS files
110 1381
        if (isset($documentAnnot->bucketName)) {
111 58
            $class->setBucketName($documentAnnot->bucketName);
112
        }
113 1381
        if (isset($documentAnnot->repositoryClass)) {
114 60
            $class->setCustomRepositoryClass($documentAnnot->repositoryClass);
115
        }
116 1381
        if (isset($documentAnnot->writeConcern)) {
117 10
            $class->setWriteConcern($documentAnnot->writeConcern);
118
        }
119 1381
        if (isset($documentAnnot->indexes)) {
120 1380
            foreach ($documentAnnot->indexes as $index) {
121
                $this->addIndex($class, $index);
122
            }
123
        }
124 1381
        if (! empty($documentAnnot->readOnly)) {
125 5
            $class->markReadOnly();
126
        }
127
128 1381
        foreach ($reflClass->getProperties() as $property) {
129 1380
            if (($class->isMappedSuperclass && ! $property->isPrivate())
130
                ||
131 1380
                ($class->isInheritedField($property->name) && $property->getDeclaringClass()->name !== $class->name)) {
132 871
                continue;
133
            }
134
135 1379
            $indexes = [];
136 1379
            $mapping = ['fieldName' => $property->getName()];
137 1379
            $fieldAnnot = null;
138
139 1379
            foreach ($this->reader->getPropertyAnnotations($property) as $annot) {
140 1379
                if ($annot instanceof ODM\AbstractField) {
141 1379
                    $fieldAnnot = $annot;
142 1379
                    if ($annot->isDeprecated()) {
143
                        @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...
144
                    }
145
                }
146 1379
                if ($annot instanceof ODM\AbstractIndex) {
147 162
                    $indexes[] = $annot;
148
                }
149 1379
                if ($annot instanceof ODM\Indexes) {
150
                    foreach (is_array($annot->value) ? $annot->value : [$annot->value] as $index) {
151
                        $indexes[] = $index;
152
                    }
153 1379
                } elseif ($annot instanceof ODM\AlsoLoad) {
154 15
                    $mapping['alsoLoadFields'] = (array) $annot->value;
155 1379
                } elseif ($annot instanceof ODM\Version) {
156 70
                    $mapping['version'] = true;
157 1379
                } elseif ($annot instanceof ODM\Lock) {
158 1379
                    $mapping['lock'] = true;
159
                }
160
            }
161
162 1379
            if ($fieldAnnot) {
163 1379
                $mapping = array_replace($mapping, (array) $fieldAnnot);
164 1379
                $class->mapField($mapping);
165
            }
166
167 1379
            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...
168 1379
                continue;
169
            }
170
171 162
            foreach ($indexes as $index) {
172 162
                $name = $mapping['name'] ?? $mapping['fieldName'];
173 162
                $keys = [$name => $index->order ?: 'asc'];
174 162
                $this->addIndex($class, $index, $keys);
175
            }
176
        }
177
178
        // Set shard key after all fields to ensure we mapped all its keys
179 1379
        if (isset($classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey'])) {
180 62
            $this->setShardKey($class, $classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey']);
181
        }
182
183
        /** @var \ReflectionMethod $method */
184 1378
        foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
185
            /* Filter for the declaring class only. Callbacks from parent
186
             * classes will already be registered.
187
             */
188 1139
            if ($method->getDeclaringClass()->name !== $reflClass->name) {
189 836
                continue;
190
            }
191
192 1139
            foreach ($this->reader->getMethodAnnotations($method) as $annot) {
193 826
                if ($annot instanceof ODM\AlsoLoad) {
194 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...
195
                }
196
197 826
                if (! isset($classAnnotations[ODM\HasLifecycleCallbacks::class])) {
198 67
                    continue;
199
                }
200
201 807
                if ($annot instanceof ODM\PrePersist) {
202 792
                    $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...
203 74
                } elseif ($annot instanceof ODM\PostPersist) {
204 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...
205 74
                } elseif ($annot instanceof ODM\PreUpdate) {
206 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...
207 69
                } elseif ($annot instanceof ODM\PostUpdate) {
208 58
                    $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...
209 67
                } elseif ($annot instanceof ODM\PreRemove) {
210 65
                    $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...
211 67
                } elseif ($annot instanceof ODM\PostRemove) {
212 65
                    $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...
213 67
                } elseif ($annot instanceof ODM\PreLoad) {
214 66
                    $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...
215 66
                } elseif ($annot instanceof ODM\PostLoad) {
216 65
                    $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...
217 11
                } elseif ($annot instanceof ODM\PreFlush) {
218 1139
                    $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...
219
                }
220
            }
221
        }
222 1378
    }
223
224 183
    private function addIndex(ClassMetadata $class, $index, array $keys = [])
225
    {
226 183
        $keys = array_merge($keys, $index->keys);
227 183
        $options = [];
228 183
        $allowed = ['name', 'dropDups', 'background', 'unique', 'sparse', 'expireAfterSeconds'];
229 183
        foreach ($allowed as $name) {
230 183
            if (! isset($index->$name)) {
231 183
                continue;
232
            }
233
234 183
            $options[$name] = $index->$name;
235
        }
236 183
        if (! empty($index->partialFilterExpression)) {
237 2
            $options['partialFilterExpression'] = $index->partialFilterExpression;
238
        }
239 183
        $options = array_merge($options, $index->options);
240 183
        $class->addIndex($keys, $options);
241 183
    }
242
243
    /**
244
     *
245
     * @throws MappingException
246
     */
247 62
    private function setShardKey(ClassMetadata $class, ODM\ShardKey $shardKey)
248
    {
249 62
        $options = [];
250 62
        $allowed = ['unique', 'numInitialChunks'];
251 62
        foreach ($allowed as $name) {
252 62
            if (! isset($shardKey->$name)) {
253 62
                continue;
254
            }
255
256
            $options[$name] = $shardKey->$name;
257
        }
258
259 62
        $class->setShardKey($shardKey->keys, $options);
260 61
    }
261
262
    /**
263
     * Factory method for the Annotation Driver
264
     *
265
     * @param array|string $paths
266
     * @return AnnotationDriver
267
     */
268 1641
    public static function create($paths = [], ?Reader $reader = null)
269
    {
270 1641
        if ($reader === null) {
271 1641
            $reader = new AnnotationReader();
272
        }
273 1641
        return new self($reader, $paths);
274
    }
275
}
276