Completed
Push — master ( a8fe50...bce26f )
by Maciej
13s
created

ODM/MongoDB/Mapping/Driver/AnnotationDriver.php (1 issue)

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
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
It seems like $annot->value can also be of type array; however, Doctrine\ODM\MongoDB\Map...ta::setReadPreference() does only seem to accept null|string|integer, 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...
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