Completed
Pull Request — master (#1303)
by Andreas
09:52
created

AnnotationDriver::loadMetadataForClass()   F

Complexity

Conditions 61
Paths > 20000

Size

Total Lines 168
Code Lines 109

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 130
CRAP Score 61.4965

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 168
ccs 130
cts 137
cp 0.9489
rs 2
cc 61
eloc 109
nc 8697650
nop 2
crap 61.4965

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
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ODM\MongoDB\Mapping\Driver;
21
22
use Doctrine\Common\Annotations\AnnotationReader;
23
use Doctrine\Common\Annotations\AnnotationRegistry;
24
use Doctrine\Common\Annotations\Reader;
25
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
26
use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver;
27
use Doctrine\ODM\MongoDB\Events;
28
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
29
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata as MappingClassMetadata;
30
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo;
31
use Doctrine\ODM\MongoDB\Mapping\MappingException;
32
33
/**
34
 * The AnnotationDriver reads the mapping metadata from docblock annotations.
35
 *
36
 * @since       1.0
37
 * @author      Jonathan H. Wage <[email protected]>
38
 * @author      Roman Borschel <[email protected]>
39
 */
40
class AnnotationDriver extends AbstractAnnotationDriver
41
{
42
    protected $entityAnnotationClasses = array(
43
        ODM\Document::class         => 1,
44
        ODM\MappedSuperclass::class => 2,
45
        ODM\EmbeddedDocument::class => 3,
46
    );
47
48
    /**
49
     * Registers annotation classes to the common registry.
50
     *
51
     * This method should be called when bootstrapping your application.
52
     */
53
    public static function registerAnnotationClasses()
54
    {
55
        AnnotationRegistry::registerFile(__DIR__ . '/../Annotations/DoctrineAnnotations.php');
56
    }
57
58
    /**
59
     * {@inheritdoc}
60
     */
61 765
    public function loadMetadataForClass($className, ClassMetadata $class)
62
    {
63
        /** @var $class ClassMetadataInfo */
64 765
        $reflClass = $class->getReflectionClass();
65
66 765
        $classAnnotations = $this->reader->getClassAnnotations($reflClass);
67
68 765
        $documentAnnots = array();
69 765
        foreach ($classAnnotations as $annot) {
70 763
            $classAnnotations[get_class($annot)] = $annot;
71
72 763
            foreach ($this->entityAnnotationClasses as $annotClass => $i) {
73 763
                if ($annot instanceof $annotClass) {
74 763
                    $documentAnnots[$i] = $annot;
75 763
                    continue 2;
76
                }
77 502
            }
78
79
            // non-document class annotations
80 321
            if ($annot instanceof ODM\AbstractIndex) {
81 13
                $this->addIndex($class, $annot);
82 13
            }
83 321
            if ($annot instanceof ODM\Indexes) {
84 37
                foreach (is_array($annot->value) ? $annot->value : array($annot->value) as $index) {
85 37
                    $this->addIndex($class, $index);
86 37
                }
87 321
            } elseif ($annot instanceof ODM\InheritanceType) {
88 242
                $class->setInheritanceType(constant(MappingClassMetadata::class . '::INHERITANCE_TYPE_'.$annot->value));
89 303
            } elseif ($annot instanceof ODM\DiscriminatorField) {
90
                // $fieldName property is deprecated, but fall back for BC
91 75
                if (isset($annot->value)) {
92 19
                    $class->setDiscriminatorField($annot->value);
0 ignored issues
show
Bug introduced by
It seems like $annot->value can also be of type array; however, Doctrine\ODM\MongoDB\Map...setDiscriminatorField() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
93 75
                } elseif (isset($annot->name)) {
94
                    $class->setDiscriminatorField($annot->name);
95 72
                } elseif (isset($annot->fieldName)) {
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ODM\MongoDB\Map...inatorField::$fieldName has been deprecated.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
96 72
                    $class->setDiscriminatorField($annot->fieldName);
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ODM\MongoDB\Map...inatorField::$fieldName has been deprecated.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
97 72
                }
98 301
            } elseif ($annot instanceof ODM\DiscriminatorMap) {
99 75
                $class->setDiscriminatorMap($annot->value);
0 ignored issues
show
Bug introduced by
It seems like $annot->value can also be of type null or string; however, Doctrine\ODM\MongoDB\Map...::setDiscriminatorMap() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
100 300
            } elseif ($annot instanceof ODM\DiscriminatorValue) {
101
                $class->setDiscriminatorValue($annot->value);
0 ignored issues
show
Bug introduced by
It seems like $annot->value can also be of type array or null; however, Doctrine\ODM\MongoDB\Map...setDiscriminatorValue() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
102 249
            } elseif ($annot instanceof ODM\ChangeTrackingPolicy) {
103 21
                $class->setChangeTrackingPolicy(constant(MappingClassMetadata::class . '::CHANGETRACKING_'.$annot->value));
104 249
            } elseif ($annot instanceof ODM\DefaultDiscriminatorValue) {
105 22
                $class->setDefaultDiscriminatorValue($annot->value);
0 ignored issues
show
Bug introduced by
It seems like $annot->value can also be of type array or null; however, Doctrine\ODM\MongoDB\Map...ultDiscriminatorValue() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
106 22
            }
107
108 765
        }
109
110 765
        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...
111 2
            throw MappingException::classIsNotAValidDocument($className);
112
        }
113
114
        // find the winning document annotation
115 763
        ksort($documentAnnots);
116 763
        $documentAnnot = reset($documentAnnots);
117
118 763
        if ($documentAnnot instanceof ODM\MappedSuperclass) {
119 241
            $class->isMappedSuperclass = true;
120 763
        } elseif ($documentAnnot instanceof ODM\EmbeddedDocument) {
121 399
            $class->isEmbeddedDocument = true;
122 399
        }
123 763
        if (isset($documentAnnot->db)) {
124 1
            $class->setDatabase($documentAnnot->db);
125 1
        }
126 763
        if (isset($documentAnnot->collection)) {
127 278
            $class->setCollection($documentAnnot->collection);
128 278
        }
129 763
        if (isset($documentAnnot->repositoryClass) && !$class->isEmbeddedDocument) {
130 31
            $class->setCustomRepositoryClass($documentAnnot->repositoryClass);
131 31
        }
132 763
        if (isset($documentAnnot->indexes)) {
133 762
            foreach ($documentAnnot->indexes as $index) {
134
                $this->addIndex($class, $index);
135 762
            }
136 762
        }
137 763
        if (isset($documentAnnot->requireIndexes)) {
138 756
            $class->setRequireIndexes($documentAnnot->requireIndexes);
139 756
        }
140 763
        if (isset($documentAnnot->slaveOkay)) {
141 1
            $class->setSlaveOkay($documentAnnot->slaveOkay);
142 1
        }
143
144 763
        foreach ($reflClass->getProperties() as $property) {
145 762
            if (($class->isMappedSuperclass && ! $property->isPrivate())
146
                ||
147 762
                ($class->isInheritedField($property->name) && $property->getDeclaringClass()->name !== $class->name)) {
148 273
                continue;
149
            }
150
151 761
            $indexes = array();
152 761
            $mapping = array('fieldName' => $property->getName());
153 761
            $fieldAnnot = null;
154
155 761
            foreach ($this->reader->getPropertyAnnotations($property) as $annot) {
156 761
                if ($annot instanceof ODM\AbstractField) {
157 761
                    $fieldAnnot = $annot;
158 761
                }
159 761
                if ($annot instanceof ODM\AbstractIndex) {
160 149
                    $indexes[] = $annot;
161 149
                }
162 761
                if ($annot instanceof ODM\Indexes) {
163
                    foreach (is_array($annot->value) ? $annot->value : array($annot->value) as $index) {
164
                        $indexes[] = $index;
165
                    }
166 761
                } elseif ($annot instanceof ODM\AlsoLoad) {
167 13
                    $mapping['alsoLoadFields'] = (array) $annot->value;
168 761
                } elseif ($annot instanceof ODM\Version) {
169 61
                    $mapping['version'] = true;
170 761
                } elseif ($annot instanceof ODM\Lock) {
171 24
                    $mapping['lock'] = true;
172 24
                }
173 761
            }
174
175 761
            if ($fieldAnnot) {
176 761
                $mapping = array_replace($mapping, (array) $fieldAnnot);
177 761
                $class->mapField($mapping);
178 761
            }
179
180 761
            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...
181 149
                foreach ($indexes as $index) {
182 149
                    $name = isset($mapping['name']) ? $mapping['name'] : $mapping['fieldName'];
183 150
                    $keys = array($name => $index->order ?: 'asc');
184 149
                    $this->addIndex($class, $index, $keys);
185 149
                }
186 149
            }
187 763
        }
188
189
        /** @var $method \ReflectionMethod */
190 761
        foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
191
            /* Filter for the declaring class only. Callbacks from parent
192
             * classes will already be registered.
193
             */
194 527
            if ($method->getDeclaringClass()->name !== $reflClass->name) {
195 258
                continue;
196
            }
197
198 527
            foreach ($this->reader->getMethodAnnotations($method) as $annot) {
199 247
                if ($annot instanceof ODM\AlsoLoad) {
200 13
                    $class->registerAlsoLoadMethod($method->getName(), $annot->value);
0 ignored issues
show
Bug introduced by
It seems like $annot->value can also be of type null; however, Doctrine\ODM\MongoDB\Map...egisterAlsoLoadMethod() does only seem to accept array|string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
201 13
                }
202
203 247
                if ( ! isset($classAnnotations[ODM\HasLifecycleCallbacks::class])) {
204 37
                    continue;
205
                }
206
207 228
                if ($annot instanceof ODM\PrePersist) {
208 207
                    $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...
209 228
                } elseif ($annot instanceof ODM\PostPersist) {
210 11
                    $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...
211 51
                } elseif ($annot instanceof ODM\PreUpdate) {
212 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...
213 50
                } elseif ($annot instanceof ODM\PostUpdate) {
214 32
                    $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...
215 45
                } elseif ($annot instanceof ODM\PreRemove) {
216 37
                    $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...
217 39
                } elseif ($annot instanceof ODM\PostRemove) {
218 35
                    $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...
219 39
                } elseif ($annot instanceof ODM\PreLoad) {
220 36
                    $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...
221 39
                } elseif ($annot instanceof ODM\PostLoad) {
222 37
                    $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...
223 38
                } elseif ($annot instanceof ODM\PreFlush) {
224 11
                    $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...
225 11
                }
226 527
            }
227 761
        }
228 761
    }
229
230 180
    private function addIndex(ClassMetadataInfo $class, $index, array $keys = array())
231
    {
232 180
        $keys = array_merge($keys, $index->keys);
233 180
        $options = array();
234 180
        $allowed = array('name', 'dropDups', 'background', 'safe', 'unique', 'sparse', 'expireAfterSeconds');
235 180
        foreach ($allowed as $name) {
236 180
            if (isset($index->$name)) {
237 180
                $options[$name] = $index->$name;
238 180
            }
239 180
        }
240 180
        if (! empty($index->partialFilterExpression)) {
241 2
            $options['partialFilterExpression'] = $index->partialFilterExpression;
242 2
        }
243 180
        $options = array_merge($options, $index->options);
244 180
        $class->addIndex($keys, $options);
245 180
    }
246
247
    /**
248
     * Factory method for the Annotation Driver
249
     *
250
     * @param array|string $paths
251
     * @param Reader $reader
252
     * @return AnnotationDriver
253
     */
254 945
    public static function create($paths = array(), Reader $reader = null)
255
    {
256 945
        if ($reader === null) {
257 945
            $reader = new AnnotationReader();
258 945
        }
259 945
        return new self($reader, $paths);
0 ignored issues
show
Compatibility introduced by
$reader of type object<Doctrine\Common\Annotations\Reader> is not a sub-type of object<Doctrine\Common\A...tions\AnnotationReader>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Annotations\Reader to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
260
    }
261
}
262