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

ODM/MongoDB/Mapping/Driver/AnnotationDriver.php (6 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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);
0 ignored issues
show
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...
61 891
            } elseif ($annot instanceof ODM\DiscriminatorMap) {
62 111
                $class->setDiscriminatorMap($annot->value);
0 ignored issues
show
It seems like $annot->value can also be of type 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...
63 830
            } elseif ($annot instanceof ODM\DiscriminatorValue) {
64
                $class->setDiscriminatorValue($annot->value);
0 ignored issues
show
It seems like $annot->value can also be of type array; 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...
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);
0 ignored issues
show
It seems like $annot->value can also be of type array; 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...
69 823
            } elseif ($annot instanceof ODM\ReadPreference) {
70 909
                $class->setReadPreference($annot->value, $annot->tags);
0 ignored issues
show
It seems like $annot->value can also be of type array; however, Doctrine\ODM\MongoDB\Map...ta::setReadPreference() 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...
71
            }
72
73
        }
74
75 1365
        if ( ! $documentAnnots) {
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);
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) {
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 $method \ReflectionMethod */
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);
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);
184 71
                } elseif ($annot instanceof ODM\PostPersist) {
185 10
                    $class->addLifecycleCallback($method->getName(), Events::postPersist);
186 71
                } elseif ($annot instanceof ODM\PreUpdate) {
187 15
                    $class->addLifecycleCallback($method->getName(), Events::preUpdate);
188 66
                } elseif ($annot instanceof ODM\PostUpdate) {
189 55
                    $class->addLifecycleCallback($method->getName(), Events::postUpdate);
190 64
                } elseif ($annot instanceof ODM\PreRemove) {
191 62
                    $class->addLifecycleCallback($method->getName(), Events::preRemove);
192 64
                } elseif ($annot instanceof ODM\PostRemove) {
193 62
                    $class->addLifecycleCallback($method->getName(), Events::postRemove);
194 64
                } elseif ($annot instanceof ODM\PreLoad) {
195 63
                    $class->addLifecycleCallback($method->getName(), Events::preLoad);
196 63
                } elseif ($annot instanceof ODM\PostLoad) {
197 62
                    $class->addLifecycleCallback($method->getName(), Events::postLoad);
198 11
                } elseif ($annot instanceof ODM\PreFlush) {
199 1125
                    $class->addLifecycleCallback($method->getName(), Events::preFlush);
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);
0 ignored issues
show
$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...
254
    }
255
}
256