These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace Doctrine\ODM\MongoDB\Mapping\Driver; |
||
6 | |||
7 | use Doctrine\Common\Annotations\AnnotationReader; |
||
8 | use Doctrine\Common\Annotations\Reader; |
||
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\Annotations\AbstractIndex; |
||
13 | use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; |
||
14 | use Doctrine\ODM\MongoDB\Mapping\MappingException; |
||
15 | use const E_USER_DEPRECATED; |
||
16 | use function array_merge; |
||
17 | use function array_replace; |
||
18 | use function constant; |
||
19 | use function get_class; |
||
20 | use function is_array; |
||
21 | use function ksort; |
||
22 | use function reset; |
||
23 | use function trigger_error; |
||
24 | |||
25 | /** |
||
26 | * The AnnotationDriver reads the mapping metadata from docblock annotations. |
||
27 | * |
||
28 | */ |
||
29 | class AnnotationDriver extends AbstractAnnotationDriver |
||
30 | { |
||
31 | /** @var int[] */ |
||
32 | protected $entityAnnotationClasses = [ |
||
33 | ODM\Document::class => 1, |
||
34 | ODM\MappedSuperclass::class => 2, |
||
35 | ODM\EmbeddedDocument::class => 3, |
||
36 | ODM\QueryResultDocument::class => 4, |
||
37 | ODM\File::class => 5, |
||
38 | ]; |
||
39 | |||
40 | /** |
||
41 | * {@inheritdoc} |
||
42 | */ |
||
43 | 1408 | public function loadMetadataForClass($className, \Doctrine\Common\Persistence\Mapping\ClassMetadata $class): void |
|
44 | { |
||
45 | /** @var ClassMetadata $class */ |
||
46 | 1408 | $reflClass = $class->getReflectionClass(); |
|
47 | |||
48 | 1408 | $classAnnotations = $this->reader->getClassAnnotations($reflClass); |
|
49 | |||
50 | 1408 | $documentAnnots = []; |
|
51 | 1408 | foreach ($classAnnotations as $annot) { |
|
52 | 1406 | $classAnnotations[get_class($annot)] = $annot; |
|
53 | |||
54 | 1406 | foreach ($this->entityAnnotationClasses as $annotClass => $i) { |
|
55 | 1406 | if ($annot instanceof $annotClass) { |
|
56 | 1406 | $documentAnnots[$i] = $annot; |
|
57 | 1406 | continue 2; |
|
58 | } |
||
59 | } |
||
60 | |||
61 | // non-document class annotations |
||
62 | 937 | if ($annot instanceof ODM\AbstractIndex) { |
|
63 | 6 | $this->addIndex($class, $annot); |
|
64 | } |
||
65 | 937 | if ($annot instanceof ODM\Indexes) { |
|
66 | 89 | foreach (is_array($annot->value) ? $annot->value : [$annot->value] as $index) { |
|
67 | 89 | $this->addIndex($class, $index); |
|
68 | } |
||
69 | 922 | } elseif ($annot instanceof ODM\InheritanceType) { |
|
70 | 858 | $class->setInheritanceType(constant(ClassMetadata::class . '::INHERITANCE_TYPE_' . $annot->value)); |
|
71 | 920 | } elseif ($annot instanceof ODM\DiscriminatorField) { |
|
72 | 131 | $class->setDiscriminatorField($annot->value); |
|
73 | 919 | } elseif ($annot instanceof ODM\DiscriminatorMap) { |
|
74 | 131 | $class->setDiscriminatorMap($annot->value); |
|
75 | 858 | } elseif ($annot instanceof ODM\DiscriminatorValue) { |
|
76 | $class->setDiscriminatorValue($annot->value); |
||
77 | 858 | } elseif ($annot instanceof ODM\ChangeTrackingPolicy) { |
|
78 | 66 | $class->setChangeTrackingPolicy(constant(ClassMetadata::class . '::CHANGETRACKING_' . $annot->value)); |
|
79 | 856 | } elseif ($annot instanceof ODM\DefaultDiscriminatorValue) { |
|
80 | 68 | $class->setDefaultDiscriminatorValue($annot->value); |
|
81 | 851 | } elseif ($annot instanceof ODM\ReadPreference) { |
|
82 | 937 | $class->setReadPreference($annot->value, $annot->tags); |
|
0 ignored issues
–
show
|
|||
83 | } |
||
84 | } |
||
85 | |||
86 | 1408 | if (! $documentAnnots) { |
|
87 | 3 | throw MappingException::classIsNotAValidDocument($className); |
|
88 | } |
||
89 | |||
90 | // find the winning document annotation |
||
91 | 1406 | ksort($documentAnnots); |
|
92 | 1406 | $documentAnnot = reset($documentAnnots); |
|
93 | |||
94 | 1406 | if ($documentAnnot instanceof ODM\MappedSuperclass) { |
|
95 | 848 | $class->isMappedSuperclass = true; |
|
96 | 1405 | } elseif ($documentAnnot instanceof ODM\EmbeddedDocument) { |
|
97 | 260 | $class->isEmbeddedDocument = true; |
|
98 | 1398 | } elseif ($documentAnnot instanceof ODM\QueryResultDocument) { |
|
99 | 67 | $class->isQueryResultDocument = true; |
|
100 | 1398 | } elseif ($documentAnnot instanceof ODM\File) { |
|
101 | 78 | $class->isFile = true; |
|
102 | |||
103 | 78 | if ($documentAnnot->chunkSizeBytes !== null) { |
|
104 | 77 | $class->setChunkSizeBytes($documentAnnot->chunkSizeBytes); |
|
105 | } |
||
106 | } |
||
107 | |||
108 | 1406 | if (isset($documentAnnot->db)) { |
|
109 | 1 | $class->setDatabase($documentAnnot->db); |
|
110 | } |
||
111 | 1406 | if (isset($documentAnnot->collection)) { |
|
112 | 897 | $class->setCollection($documentAnnot->collection); |
|
113 | } |
||
114 | // Store bucketName as collection name for GridFS files |
||
115 | 1406 | if (isset($documentAnnot->bucketName)) { |
|
116 | 78 | $class->setBucketName($documentAnnot->bucketName); |
|
117 | } |
||
118 | 1406 | if (isset($documentAnnot->repositoryClass)) { |
|
119 | 77 | $class->setCustomRepositoryClass($documentAnnot->repositoryClass); |
|
120 | } |
||
121 | 1406 | if (isset($documentAnnot->writeConcern)) { |
|
122 | 10 | $class->setWriteConcern($documentAnnot->writeConcern); |
|
123 | } |
||
124 | 1406 | if (isset($documentAnnot->indexes)) { |
|
125 | 1405 | foreach ($documentAnnot->indexes as $index) { |
|
126 | $this->addIndex($class, $index); |
||
127 | } |
||
128 | } |
||
129 | 1406 | if (! empty($documentAnnot->readOnly)) { |
|
130 | 5 | $class->markReadOnly(); |
|
131 | } |
||
132 | |||
133 | 1406 | foreach ($reflClass->getProperties() as $property) { |
|
134 | 1405 | if (($class->isMappedSuperclass && ! $property->isPrivate()) |
|
135 | || |
||
136 | 1405 | ($class->isInheritedField($property->name) && $property->getDeclaringClass()->name !== $class->name)) { |
|
137 | 890 | continue; |
|
138 | } |
||
139 | |||
140 | 1404 | $indexes = []; |
|
141 | 1404 | $mapping = ['fieldName' => $property->getName()]; |
|
142 | 1404 | $fieldAnnot = null; |
|
143 | |||
144 | 1404 | foreach ($this->reader->getPropertyAnnotations($property) as $annot) { |
|
145 | 1404 | if ($annot instanceof ODM\AbstractField) { |
|
146 | 1404 | $fieldAnnot = $annot; |
|
147 | 1404 | if ($annot->isDeprecated()) { |
|
148 | @trigger_error($annot->getDeprecationMessage(), E_USER_DEPRECATED); |
||
149 | } |
||
150 | } |
||
151 | 1404 | if ($annot instanceof ODM\AbstractIndex) { |
|
152 | 179 | $indexes[] = $annot; |
|
153 | } |
||
154 | 1404 | if ($annot instanceof ODM\Indexes) { |
|
155 | foreach (is_array($annot->value) ? $annot->value : [$annot->value] as $index) { |
||
156 | $indexes[] = $index; |
||
157 | } |
||
158 | 1404 | } elseif ($annot instanceof ODM\AlsoLoad) { |
|
159 | 15 | $mapping['alsoLoadFields'] = (array) $annot->value; |
|
160 | 1404 | } elseif ($annot instanceof ODM\Version) { |
|
161 | 87 | $mapping['version'] = true; |
|
162 | 1404 | } elseif ($annot instanceof ODM\Lock) { |
|
163 | 1404 | $mapping['lock'] = true; |
|
164 | } |
||
165 | } |
||
166 | |||
167 | 1404 | if ($fieldAnnot) { |
|
168 | 1404 | $mapping = array_replace($mapping, (array) $fieldAnnot); |
|
169 | 1404 | $class->mapField($mapping); |
|
170 | } |
||
171 | |||
172 | 1404 | if (! $indexes) { |
|
173 | 1404 | continue; |
|
174 | } |
||
175 | |||
176 | 179 | foreach ($indexes as $index) { |
|
177 | 179 | $name = $mapping['name'] ?? $mapping['fieldName']; |
|
178 | 179 | $keys = [$name => $index->order ?: 'asc']; |
|
179 | 179 | $this->addIndex($class, $index, $keys); |
|
180 | } |
||
181 | } |
||
182 | |||
183 | // Set shard key after all fields to ensure we mapped all its keys |
||
184 | 1404 | if (isset($classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey'])) { |
|
185 | 82 | $this->setShardKey($class, $classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey']); |
|
186 | } |
||
187 | |||
188 | /** @var \ReflectionMethod $method */ |
||
189 | 1403 | foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { |
|
190 | /* Filter for the declaring class only. Callbacks from parent |
||
191 | * classes will already be registered. |
||
192 | */ |
||
193 | 1161 | if ($method->getDeclaringClass()->name !== $reflClass->name) { |
|
194 | 855 | continue; |
|
195 | } |
||
196 | |||
197 | 1161 | foreach ($this->reader->getMethodAnnotations($method) as $annot) { |
|
198 | 845 | if ($annot instanceof ODM\AlsoLoad) { |
|
199 | 13 | $class->registerAlsoLoadMethod($method->getName(), $annot->value); |
|
200 | } |
||
201 | |||
202 | 845 | if (! isset($classAnnotations[ODM\HasLifecycleCallbacks::class])) { |
|
203 | 84 | continue; |
|
204 | } |
||
205 | |||
206 | 826 | if ($annot instanceof ODM\PrePersist) { |
|
207 | 811 | $class->addLifecycleCallback($method->getName(), Events::prePersist); |
|
208 | 91 | } elseif ($annot instanceof ODM\PostPersist) { |
|
209 | 10 | $class->addLifecycleCallback($method->getName(), Events::postPersist); |
|
210 | 91 | } elseif ($annot instanceof ODM\PreUpdate) { |
|
211 | 15 | $class->addLifecycleCallback($method->getName(), Events::preUpdate); |
|
212 | 86 | } elseif ($annot instanceof ODM\PostUpdate) { |
|
213 | 75 | $class->addLifecycleCallback($method->getName(), Events::postUpdate); |
|
214 | 84 | } elseif ($annot instanceof ODM\PreRemove) { |
|
215 | 82 | $class->addLifecycleCallback($method->getName(), Events::preRemove); |
|
216 | 84 | } elseif ($annot instanceof ODM\PostRemove) { |
|
217 | 82 | $class->addLifecycleCallback($method->getName(), Events::postRemove); |
|
218 | 84 | } elseif ($annot instanceof ODM\PreLoad) { |
|
219 | 83 | $class->addLifecycleCallback($method->getName(), Events::preLoad); |
|
220 | 83 | } elseif ($annot instanceof ODM\PostLoad) { |
|
221 | 82 | $class->addLifecycleCallback($method->getName(), Events::postLoad); |
|
222 | 11 | } elseif ($annot instanceof ODM\PreFlush) { |
|
223 | 1161 | $class->addLifecycleCallback($method->getName(), Events::preFlush); |
|
224 | } |
||
225 | } |
||
226 | } |
||
227 | 1403 | } |
|
228 | |||
229 | 206 | private function addIndex(ClassMetadata $class, AbstractIndex $index, array $keys = []): void |
|
230 | { |
||
231 | 206 | $keys = array_merge($keys, $index->keys); |
|
232 | 206 | $options = []; |
|
233 | 206 | $allowed = ['name', 'dropDups', 'background', 'unique', 'sparse', 'expireAfterSeconds']; |
|
234 | 206 | foreach ($allowed as $name) { |
|
235 | 206 | if (! isset($index->$name)) { |
|
236 | 206 | continue; |
|
237 | } |
||
238 | |||
239 | 206 | $options[$name] = $index->$name; |
|
240 | } |
||
241 | 206 | if (! empty($index->partialFilterExpression)) { |
|
242 | 2 | $options['partialFilterExpression'] = $index->partialFilterExpression; |
|
243 | } |
||
244 | 206 | $options = array_merge($options, $index->options); |
|
245 | 206 | $class->addIndex($keys, $options); |
|
246 | 206 | } |
|
247 | |||
248 | /** |
||
249 | * |
||
250 | * @throws MappingException |
||
251 | */ |
||
252 | 82 | private function setShardKey(ClassMetadata $class, ODM\ShardKey $shardKey): void |
|
253 | { |
||
254 | 82 | $options = []; |
|
255 | 82 | $allowed = ['unique', 'numInitialChunks']; |
|
256 | 82 | foreach ($allowed as $name) { |
|
257 | 82 | if (! isset($shardKey->$name)) { |
|
258 | 82 | continue; |
|
259 | } |
||
260 | |||
261 | $options[$name] = $shardKey->$name; |
||
262 | } |
||
263 | |||
264 | 82 | $class->setShardKey($shardKey->keys, $options); |
|
265 | 81 | } |
|
266 | |||
267 | /** |
||
268 | * Factory method for the Annotation Driver |
||
269 | * |
||
270 | * @param array|string $paths |
||
271 | */ |
||
272 | 1667 | public static function create($paths = [], ?Reader $reader = null): AnnotationDriver |
|
273 | { |
||
274 | 1667 | if ($reader === null) { |
|
275 | 1667 | $reader = new AnnotationReader(); |
|
276 | } |
||
277 | 1667 | return new self($reader, $paths); |
|
278 | } |
||
279 | } |
||
280 |
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:
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.