Completed
Push — master ( 6f1066...9b6aac )
by Maciej
44:22
created

AnnotationDriver::create()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2
Metric Value
dl 0
loc 7
ccs 5
cts 5
cp 1
rs 9.4286
cc 2
eloc 4
nc 2
nop 2
crap 2
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
 */
38
class AnnotationDriver extends AbstractAnnotationDriver
39
{
40
    protected $entityAnnotationClasses = array(
41
        ODM\Document::class         => 1,
42
        ODM\MappedSuperclass::class => 2,
43
        ODM\EmbeddedDocument::class => 3,
44
    );
45
46
    /**
47
     * Registers annotation classes to the common registry.
48
     *
49
     * This method should be called when bootstrapping your application.
50
     */
51
    public static function registerAnnotationClasses()
52
    {
53
        AnnotationRegistry::registerFile(__DIR__ . '/../Annotations/DoctrineAnnotations.php');
54
    }
55
56
    /**
57
     * {@inheritdoc}
58
     */
59 801
    public function loadMetadataForClass($className, ClassMetadata $class)
60
    {
61
        /** @var $class ClassMetadataInfo */
62 801
        $reflClass = $class->getReflectionClass();
63
64 801
        $classAnnotations = $this->reader->getClassAnnotations($reflClass);
65
66 801
        $documentAnnots = array();
67 801
        foreach ($classAnnotations as $annot) {
68 799
            $classAnnotations[get_class($annot)] = $annot;
69
70 799
            foreach ($this->entityAnnotationClasses as $annotClass => $i) {
71 799
                if ($annot instanceof $annotClass) {
72 799
                    $documentAnnots[$i] = $annot;
73 799
                    continue 2;
74
                }
75 537
            }
76
77
            // non-document class annotations
78 356
            if ($annot instanceof ODM\AbstractIndex) {
79 13
                $this->addIndex($class, $annot);
80 13
            }
81 356
            if ($annot instanceof ODM\Indexes) {
82 66
                foreach (is_array($annot->value) ? $annot->value : array($annot->value) as $index) {
83 66
                    $this->addIndex($class, $index);
84 66
                }
85 356
            } elseif ($annot instanceof ODM\InheritanceType) {
86 277
                $class->setInheritanceType(constant(MappingClassMetadata::class . '::INHERITANCE_TYPE_'.$annot->value));
87 338
            } elseif ($annot instanceof ODM\DiscriminatorField) {
88
                // $fieldName property is deprecated, but fall back for BC
89 110
                if (isset($annot->value)) {
90 48
                    $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...
91 110
                } elseif (isset($annot->name)) {
92
                    $class->setDiscriminatorField($annot->name);
93 107
                } 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...
94 107
                    $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...
95 107
                }
96 336
            } elseif ($annot instanceof ODM\DiscriminatorMap) {
97 110
                $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...
98 335
            } elseif ($annot instanceof ODM\DiscriminatorValue) {
99
                $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...
100 278
            } elseif ($annot instanceof ODM\ChangeTrackingPolicy) {
101 50
                $class->setChangeTrackingPolicy(constant(MappingClassMetadata::class . '::CHANGETRACKING_'.$annot->value));
102 278
            } elseif ($annot instanceof ODM\DefaultDiscriminatorValue) {
103 51
                $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...
104 51
            }
105
106 801
        }
107
108 801
        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...
109 2
            throw MappingException::classIsNotAValidDocument($className);
110
        }
111
112
        // find the winning document annotation
113 799
        ksort($documentAnnots);
114 799
        $documentAnnot = reset($documentAnnots);
115
116 799
        if ($documentAnnot instanceof ODM\MappedSuperclass) {
117 270
            $class->isMappedSuperclass = true;
118 799
        } elseif ($documentAnnot instanceof ODM\EmbeddedDocument) {
119 428
            $class->isEmbeddedDocument = true;
120 428
        }
121 799
        if (isset($documentAnnot->db)) {
122 1
            $class->setDatabase($documentAnnot->db);
123 1
        }
124 799
        if (isset($documentAnnot->collection)) {
125 308
            $class->setCollection($documentAnnot->collection);
126 308
        }
127 799
        if (isset($documentAnnot->repositoryClass) && !$class->isEmbeddedDocument) {
128 60
            $class->setCustomRepositoryClass($documentAnnot->repositoryClass);
129 60
        }
130 799
        if (isset($documentAnnot->indexes)) {
131 798
            foreach ($documentAnnot->indexes as $index) {
132
                $this->addIndex($class, $index);
133 798
            }
134 798
        }
135 799
        if (isset($documentAnnot->requireIndexes)) {
136 792
            $class->setRequireIndexes($documentAnnot->requireIndexes);
137 792
        }
138 799
        if (isset($documentAnnot->slaveOkay)) {
139 1
            $class->setSlaveOkay($documentAnnot->slaveOkay);
140 1
        }
141
142 799
        foreach ($reflClass->getProperties() as $property) {
143 798
            if (($class->isMappedSuperclass && ! $property->isPrivate())
144
                ||
145 798
                ($class->isInheritedField($property->name) && $property->getDeclaringClass()->name !== $class->name)) {
146 308
                continue;
147
            }
148
149 797
            $indexes = array();
150 797
            $mapping = array('fieldName' => $property->getName());
151 797
            $fieldAnnot = null;
152
153 797
            foreach ($this->reader->getPropertyAnnotations($property) as $annot) {
154 797
                if ($annot instanceof ODM\AbstractField) {
155 797
                    $fieldAnnot = $annot;
156 797
                }
157 797
                if ($annot instanceof ODM\AbstractIndex) {
158 178
                    $indexes[] = $annot;
159 178
                }
160 797
                if ($annot instanceof ODM\Indexes) {
161
                    foreach (is_array($annot->value) ? $annot->value : array($annot->value) as $index) {
162
                        $indexes[] = $index;
163
                    }
164 797
                } elseif ($annot instanceof ODM\AlsoLoad) {
165 13
                    $mapping['alsoLoadFields'] = (array) $annot->value;
166 797
                } elseif ($annot instanceof ODM\Version) {
167 90
                    $mapping['version'] = true;
168 797
                } elseif ($annot instanceof ODM\Lock) {
169 24
                    $mapping['lock'] = true;
170 24
                }
171 797
            }
172
173 797
            if ($fieldAnnot) {
174 797
                $mapping = array_replace($mapping, (array) $fieldAnnot);
175 797
                $class->mapField($mapping);
176 797
            }
177
178 797
            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...
179 178
                foreach ($indexes as $index) {
180 178
                    $name = isset($mapping['name']) ? $mapping['name'] : $mapping['fieldName'];
181 178
                    $keys = array($name => $index->order ?: 'asc');
182 178
                    $this->addIndex($class, $index, $keys);
183 179
                }
184 178
            }
185 799
        }
186
187
        /** @var $method \ReflectionMethod */
188 797
        foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
189
            /* Filter for the declaring class only. Callbacks from parent
190
             * classes will already be registered.
191
             */
192 563
            if ($method->getDeclaringClass()->name !== $reflClass->name) {
193 287
                continue;
194
            }
195
196 563
            foreach ($this->reader->getMethodAnnotations($method) as $annot) {
197 276
                if ($annot instanceof ODM\AlsoLoad) {
198 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...
199 13
                }
200
201 276
                if ( ! isset($classAnnotations[ODM\HasLifecycleCallbacks::class])) {
202 66
                    continue;
203
                }
204
205 257
                if ($annot instanceof ODM\PrePersist) {
206 236
                    $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...
207 257
                } elseif ($annot instanceof ODM\PostPersist) {
208 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...
209 80
                } elseif ($annot instanceof ODM\PreUpdate) {
210 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...
211 79
                } elseif ($annot instanceof ODM\PostUpdate) {
212 61
                    $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...
213 74
                } elseif ($annot instanceof ODM\PreRemove) {
214 66
                    $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...
215 68
                } elseif ($annot instanceof ODM\PostRemove) {
216 64
                    $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...
217 68
                } elseif ($annot instanceof ODM\PreLoad) {
218 65
                    $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...
219 68
                } elseif ($annot instanceof ODM\PostLoad) {
220 66
                    $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...
221 67
                } elseif ($annot instanceof ODM\PreFlush) {
222 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...
223 11
                }
224 563
            }
225 797
        }
226 797
    }
227
228 209
    private function addIndex(ClassMetadataInfo $class, $index, array $keys = array())
229
    {
230 209
        $keys = array_merge($keys, $index->keys);
231 209
        $options = array();
232 209
        $allowed = array('name', 'dropDups', 'background', 'safe', 'unique', 'sparse', 'expireAfterSeconds');
233 209
        foreach ($allowed as $name) {
234 209
            if (isset($index->$name)) {
235 209
                $options[$name] = $index->$name;
236 209
            }
237 209
        }
238 209
        if (! empty($index->partialFilterExpression)) {
239 2
            $options['partialFilterExpression'] = $index->partialFilterExpression;
240 2
        }
241 209
        $options = array_merge($options, $index->options);
242 209
        $class->addIndex($keys, $options);
243 209
    }
244
245
    /**
246
     * Factory method for the Annotation Driver
247
     *
248
     * @param array|string $paths
249
     * @param Reader $reader
250
     * @return AnnotationDriver
251
     */
252 981
    public static function create($paths = array(), Reader $reader = null)
253
    {
254 981
        if ($reader === null) {
255 981
            $reader = new AnnotationReader();
256 981
        }
257 981
        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...
258
    }
259
}
260