Completed
Push — master ( 420611...4009e3 )
by Andreas
11s
created

AnnotationDriver   C

Complexity

Total Complexity 69

Size/Duplication

Total Lines 237
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 96.32%

Importance

Changes 0
Metric Value
wmc 69
lcom 1
cbo 5
dl 0
loc 237
ccs 131
cts 136
cp 0.9632
rs 5.6445
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
F loadMetadataForClass() 0 173 60
A addIndex() 0 16 4
A setShardKey() 0 12 3
A create() 0 7 2

How to fix   Complexity   

Complex Class

Complex classes like AnnotationDriver often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AnnotationDriver, and based on these observations, apply Extract Interface, too.

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