Completed
Pull Request — master (#1714)
by
unknown
10:22
created

AnnotationDriver   C

Complexity

Total Complexity 70

Size/Duplication

Total Lines 240
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 95.65%

Importance

Changes 0
Metric Value
wmc 70
lcom 1
cbo 5
dl 0
loc 240
ccs 132
cts 138
cp 0.9565
rs 5.6163
c 0
b 0
f 0

4 Methods

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

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\ClassMetadata;
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 as MappingClassMetadata;
13
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo;
14
use Doctrine\ODM\MongoDB\Mapping\MappingException;
15
16
/**
17
 * The AnnotationDriver reads the mapping metadata from docblock annotations.
18
 *
19
 * @since       1.0
20
 */
21
class AnnotationDriver extends AbstractAnnotationDriver
22
{
23
    protected $entityAnnotationClasses = array(
24
        ODM\Document::class            => 1,
25
        ODM\MappedSuperclass::class    => 2,
26
        ODM\EmbeddedDocument::class    => 3,
27
        ODM\QueryResultDocument::class => 4,
28
    );
29
30
    /**
31
     * {@inheritdoc}
32
     */
33 1378
    public function loadMetadataForClass($className, ClassMetadata $class)
34
    {
35
        /** @var $class ClassMetadataInfo */
36 1378
        $reflClass = $class->getReflectionClass();
37
38 1378
        $classAnnotations = $this->reader->getClassAnnotations($reflClass);
39
40 1378
        $documentAnnots = array();
41 1378
        foreach ($classAnnotations as $annot) {
42 1376
            $classAnnotations[get_class($annot)] = $annot;
43
44 1376
            foreach ($this->entityAnnotationClasses as $annotClass => $i) {
45 1376
                if ($annot instanceof $annotClass) {
46 1376
                    $documentAnnots[$i] = $annot;
47 1376
                    continue 2;
48
                }
49
            }
50
51
            // non-document class annotations
52 917
            if ($annot instanceof ODM\AbstractIndex) {
53 6
                $this->addIndex($class, $annot);
54
            }
55 917
            if ($annot instanceof ODM\Indexes) {
56 62
                foreach (is_array($annot->value) ? $annot->value : array($annot->value) as $index) {
57 62
                    $this->addIndex($class, $index);
58
                }
59 901
            } elseif ($annot instanceof ODM\InheritanceType) {
60 840
                $class->setInheritanceType(constant(MappingClassMetadata::class . '::INHERITANCE_TYPE_'.$annot->value));
61 899
            } elseif ($annot instanceof ODM\DiscriminatorField) {
62 111
                $class->setDiscriminatorField($annot->value);
63 898
            } elseif ($annot instanceof ODM\DiscriminatorMap) {
64 111
                $class->setDiscriminatorMap($annot->value);
65 837
            } elseif ($annot instanceof ODM\DiscriminatorValue) {
66
                $class->setDiscriminatorValue($annot->value);
67 837
            } elseif ($annot instanceof ODM\ChangeTrackingPolicy) {
68 46
                $class->setChangeTrackingPolicy(constant(MappingClassMetadata::class . '::CHANGETRACKING_'.$annot->value));
69 835
            } elseif ($annot instanceof ODM\DefaultDiscriminatorValue) {
70 48
                $class->setDefaultDiscriminatorValue($annot->value);
71 830
            } elseif ($annot instanceof ODM\ReadPreference) {
72 917
                $class->setReadPreference($annot->value, $annot->tags);
73
            }
74
75
        }
76
77 1378
        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...
78 3
            throw MappingException::classIsNotAValidDocument($className);
79
        }
80
81
        // find the winning document annotation
82 1376
        ksort($documentAnnots);
83 1376
        $documentAnnot = reset($documentAnnots);
84
85 1376
        if ($documentAnnot instanceof ODM\MappedSuperclass) {
86 829
            $class->isMappedSuperclass = true;
87 1375
        } elseif ($documentAnnot instanceof ODM\EmbeddedDocument) {
88 234
            $class->isEmbeddedDocument = true;
89 1368
        } elseif ($documentAnnot instanceof ODM\QueryResultDocument) {
90 47
            $class->isQueryResultDocument = true;
91
        }
92 1376
        if (isset($documentAnnot->db)) {
93 1
            $class->setDatabase($documentAnnot->db);
94
        }
95 1376
        if (isset($documentAnnot->collection)) {
96 874
            $class->setCollection($documentAnnot->collection);
97
        }
98 1376
        if (isset($documentAnnot->repositoryClass)) {
99 57
            $class->setCustomRepositoryClass($documentAnnot->repositoryClass);
100
        }
101 1376
        if (isset($documentAnnot->writeConcern)) {
102 10
            $class->setWriteConcern($documentAnnot->writeConcern);
103
        }
104 1376
        if (isset($documentAnnot->indexes)) {
105 1375
            foreach ($documentAnnot->indexes as $index) {
106
                $this->addIndex($class, $index);
107
            }
108
        }
109 1376
        if (isset($documentAnnot->slaveOkay)) {
110
            $class->setSlaveOkay($documentAnnot->slaveOkay);
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\ODM\MongoDB\Map...ataInfo::setSlaveOkay() has been deprecated with message: in version 1.2 and will be removed in 2.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
111
        }
112 1376
        if (! empty($documentAnnot->readOnly)) {
113 5
            $class->markReadOnly();
114
        }
115
116 1376
        foreach ($reflClass->getProperties() as $property) {
117 1375
            if (($class->isMappedSuperclass && ! $property->isPrivate())
118
                ||
119 1375
                ($class->isInheritedField($property->name) && $property->getDeclaringClass()->name !== $class->name)) {
120 871
                continue;
121
            }
122
123 1374
            $indexes = array();
124 1374
            $mapping = array('fieldName' => $property->getName());
125 1374
            $fieldAnnot = null;
126
127 1374
            foreach ($this->reader->getPropertyAnnotations($property) as $annot) {
128 1374
                if ($annot instanceof ODM\AbstractField) {
129 1374
                    $fieldAnnot = $annot;
130 1374
                    if ($annot->isDeprecated()) {
131
                        @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...
132
                    }
133
                }
134 1374
                if ($annot instanceof ODM\AbstractIndex) {
135 159
                    $indexes[] = $annot;
136
                }
137 1374
                if ($annot instanceof ODM\Indexes) {
138
                    foreach (is_array($annot->value) ? $annot->value : array($annot->value) as $index) {
139
                        $indexes[] = $index;
140
                    }
141 1374
                } elseif ($annot instanceof ODM\AlsoLoad) {
142 15
                    $mapping['alsoLoadFields'] = (array) $annot->value;
143 1374
                } elseif ($annot instanceof ODM\Version) {
144 67
                    $mapping['version'] = true;
145 1374
                } elseif ($annot instanceof ODM\Lock) {
146 1374
                    $mapping['lock'] = true;
147
                }
148
            }
149
150 1374
            if ($fieldAnnot) {
151 1374
                $mapping = array_replace($mapping, (array) $fieldAnnot);
152 1374
                $class->mapField($mapping);
153
            }
154
155 1374
            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...
156 159
                foreach ($indexes as $index) {
157 159
                    $name = $mapping['name'] ?? $mapping['fieldName'];
158 159
                    $keys = array($name => $index->order ?: 'asc');
159 1374
                    $this->addIndex($class, $index, $keys);
160
                }
161
            }
162
        }
163
164
        // Set shard key after all fields to ensure we mapped all its keys
165 1374
        if (isset($classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey'])) {
166 59
            $this->setShardKey($class, $classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey']);
167
        }
168
169
        /** @var $method \ReflectionMethod */
170 1373
        foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
171
            /* Filter for the declaring class only. Callbacks from parent
172
             * classes will already be registered.
173
             */
174 1139
            if ($method->getDeclaringClass()->name !== $reflClass->name) {
175 837
                continue;
176
            }
177
178 1139
            foreach ($this->reader->getMethodAnnotations($method) as $annot) {
179 827
                if ($annot instanceof ODM\AlsoLoad) {
180 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...
181
                }
182
183 827
                if ( ! isset($classAnnotations[ODM\HasLifecycleCallbacks::class])) {
184 64
                    continue;
185
                }
186
187 808
                if ($annot instanceof ODM\PrePersist) {
188 793
                    $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...
189 71
                } elseif ($annot instanceof ODM\PostPersist) {
190 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...
191 71
                } elseif ($annot instanceof ODM\PreUpdate) {
192 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...
193 66
                } elseif ($annot instanceof ODM\PostUpdate) {
194 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...
195 64
                } elseif ($annot instanceof ODM\PreRemove) {
196 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...
197 64
                } elseif ($annot instanceof ODM\PostRemove) {
198 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...
199 64
                } elseif ($annot instanceof ODM\PreLoad) {
200 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...
201 63
                } elseif ($annot instanceof ODM\PostLoad) {
202 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...
203 11
                } elseif ($annot instanceof ODM\PreFlush) {
204 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...
205
                }
206
            }
207
        }
208 1373
    }
209
210 181
    private function addIndex(ClassMetadataInfo $class, $index, array $keys = array())
211
    {
212 181
        $keys = array_merge($keys, $index->keys);
213 181
        $options = array();
214 181
        $allowed = array('name', 'dropDups', 'background', 'unique', 'sparse', 'expireAfterSeconds');
215 181
        foreach ($allowed as $name) {
216 181
            if (isset($index->$name)) {
217 181
                $options[$name] = $index->$name;
218
            }
219
        }
220 181
        if (! empty($index->partialFilterExpression)) {
221 2
            $options['partialFilterExpression'] = $index->partialFilterExpression;
222
        }
223 181
        $options = array_merge($options, $index->options);
224 181
        $class->addIndex($keys, $options);
225 181
    }
226
227
    /**
228
     * @param ClassMetadataInfo $class
229
     * @param ODM\ShardKey      $shardKey
230
     *
231
     * @throws MappingException
232
     */
233 59
    private function setShardKey(ClassMetadataInfo $class, ODM\ShardKey $shardKey)
234
    {
235 59
        $options = array();
236 59
        $allowed = array('unique', 'numInitialChunks');
237 59
        foreach ($allowed as $name) {
238 59
            if (isset($shardKey->$name)) {
239 59
                $options[$name] = $shardKey->$name;
240
            }
241
        }
242
243 59
        $class->setShardKey($shardKey->keys, $options);
244 58
    }
245
246
    /**
247
     * Factory method for the Annotation Driver
248
     *
249
     * @param array|string $paths
250
     * @param Reader $reader
251
     * @return AnnotationDriver
252
     */
253 1665
    public static function create($paths = array(), Reader $reader = null)
254
    {
255 1665
        if ($reader === null) {
256 1665
            $reader = new AnnotationReader();
257
        }
258 1665
        return new self($reader, $paths);
259
    }
260
}
261