Completed
Push — master ( 80e8df...60de96 )
by Maciej
18s
created

ODM/MongoDB/Mapping/Driver/AnnotationDriver.php (3 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
 * 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
        ODM\QueryResultDocument::class => 4,
45
    );
46
47
    /**
48
     * Registers annotation classes to the common registry.
49
     *
50
     * This method should be called when bootstrapping your application.
51
     */
52
    public static function registerAnnotationClasses()
53
    {
54
        AnnotationRegistry::registerFile(__DIR__ . '/../Annotations/DoctrineAnnotations.php');
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60 903
    public function loadMetadataForClass($className, ClassMetadata $class)
61
    {
62
        /** @var $class ClassMetadataInfo */
63 903
        $reflClass = $class->getReflectionClass();
64
65 903
        $classAnnotations = $this->reader->getClassAnnotations($reflClass);
66
67 903
        $documentAnnots = array();
68 903
        foreach ($classAnnotations as $annot) {
69 901
            $classAnnotations[get_class($annot)] = $annot;
70
71 901
            foreach ($this->entityAnnotationClasses as $annotClass => $i) {
72 901
                if ($annot instanceof $annotClass) {
73 901
                    $documentAnnots[$i] = $annot;
74 901
                    continue 2;
75
                }
76
            }
77
78
            // non-document class annotations
79 396
            if ($annot instanceof ODM\AbstractIndex) {
80 13
                $this->addIndex($class, $annot);
81
            }
82 396
            if ($annot instanceof ODM\Indexes) {
83 65
                foreach (is_array($annot->value) ? $annot->value : array($annot->value) as $index) {
84 65
                    $this->addIndex($class, $index);
85
                }
86 385
            } elseif ($annot instanceof ODM\InheritanceType) {
87 313
                $class->setInheritanceType(constant(MappingClassMetadata::class . '::INHERITANCE_TYPE_'.$annot->value));
88 383
            } elseif ($annot instanceof ODM\DiscriminatorField) {
89
                // $fieldName property is deprecated, but fall back for BC
90 121
                if (isset($annot->value)) {
91 121
                    $class->setDiscriminatorField($annot->value);
92
                } elseif (isset($annot->name)) {
93
                    @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
94
                        sprintf('Specifying discriminator\'s name through "name" is deprecated - use @DiscriminatorField("%s") instead', $annot->name),
95
                        E_USER_DEPRECATED
96
                    );
97
                    $class->setDiscriminatorField($annot->name);
98
                } elseif (isset($annot->fieldName)) {
99
                    @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
100
                        sprintf('Specifying discriminator\'s name through "name" is deprecated - use @DiscriminatorField("%s") instead', $annot->fieldName),
101
                        E_USER_DEPRECATED
102
                    );
103
                    $class->setDiscriminatorField($annot->fieldName);
104
                }
105 382
            } elseif ($annot instanceof ODM\DiscriminatorMap) {
106 121
                $class->setDiscriminatorMap($annot->value);
107 320
            } elseif ($annot instanceof ODM\DiscriminatorValue) {
108
                $class->setDiscriminatorValue($annot->value);
109 320
            } elseif ($annot instanceof ODM\ChangeTrackingPolicy) {
110 57
                $class->setChangeTrackingPolicy(constant(MappingClassMetadata::class . '::CHANGETRACKING_'.$annot->value));
111 317
            } elseif ($annot instanceof ODM\DefaultDiscriminatorValue) {
112 57
                $class->setDefaultDiscriminatorValue($annot->value);
113
            }
114
115
        }
116
117 903
        if ( ! $documentAnnots) {
118 3
            throw MappingException::classIsNotAValidDocument($className);
119
        }
120
121
        // find the winning document annotation
122 901
        ksort($documentAnnots);
123 901
        $documentAnnot = reset($documentAnnots);
124
125 901
        if ($documentAnnot instanceof ODM\MappedSuperclass) {
126 302
            $class->isMappedSuperclass = true;
127 900
        } elseif ($documentAnnot instanceof ODM\EmbeddedDocument) {
128 298
            $class->isEmbeddedDocument = true;
129 893
        } elseif ($documentAnnot instanceof ODM\QueryResultDocument) {
130 52
            $class->isQueryResultDocument = true;
131
        }
132 901
        if (isset($documentAnnot->db)) {
133 1
            $class->setDatabase($documentAnnot->db);
134
        }
135 901
        if (isset($documentAnnot->collection)) {
136 351
            $class->setCollection($documentAnnot->collection);
137
        }
138 901
        if (isset($documentAnnot->repositoryClass)) {
139 69
            $class->setCustomRepositoryClass($documentAnnot->repositoryClass);
140
        }
141 901
        if (isset($documentAnnot->writeConcern)) {
142 11
            $class->setWriteConcern($documentAnnot->writeConcern);
143
        }
144 901
        if (isset($documentAnnot->indexes)) {
145 900
            foreach ($documentAnnot->indexes as $index) {
146
                $this->addIndex($class, $index);
147
            }
148
        }
149 901
        if (isset($documentAnnot->requireIndexes)) {
150 893
            $class->setRequireIndexes($documentAnnot->requireIndexes);
151
        }
152 901
        if (isset($documentAnnot->slaveOkay)) {
153 1
            $class->setSlaveOkay($documentAnnot->slaveOkay);
154
        }
155
156 901
        foreach ($reflClass->getProperties() as $property) {
157 900
            if (($class->isMappedSuperclass && ! $property->isPrivate())
158
                ||
159 899
                ($class->isInheritedField($property->name) && $property->getDeclaringClass()->name !== $class->name)) {
160 347
                continue;
161
            }
162
163 899
            $indexes = array();
164 899
            $mapping = array('fieldName' => $property->getName());
165 899
            $fieldAnnot = null;
166
167 899
            foreach ($this->reader->getPropertyAnnotations($property) as $annot) {
168 899
                if ($annot instanceof ODM\AbstractField) {
169 899
                    $fieldAnnot = $annot;
170 899
                    if ($annot->isDeprecated()) {
171
                        @trigger_error($annot->getDeprecationMessage(), E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
172
                    }
173
                }
174 899
                if ($annot instanceof ODM\AbstractIndex) {
175 198
                    $indexes[] = $annot;
176
                }
177 899
                if ($annot instanceof ODM\Indexes) {
178
                    foreach (is_array($annot->value) ? $annot->value : array($annot->value) as $index) {
179
                        $indexes[] = $index;
180
                    }
181 899
                } elseif ($annot instanceof ODM\AlsoLoad) {
182 15
                    $mapping['alsoLoadFields'] = (array) $annot->value;
183 899
                } elseif ($annot instanceof ODM\Version) {
184 97
                    $mapping['version'] = true;
185 899
                } elseif ($annot instanceof ODM\Lock) {
186 24
                    $mapping['lock'] = true;
187
                }
188
            }
189
190 899
            if ($fieldAnnot) {
191 899
                $mapping = array_replace($mapping, (array) $fieldAnnot);
192 899
                $class->mapField($mapping);
193
            }
194
195 899
            if ($indexes) {
196 198
                foreach ($indexes as $index) {
197 198
                    $name = isset($mapping['name']) ? $mapping['name'] : $mapping['fieldName'];
198 198
                    $keys = array($name => $index->order ?: 'asc');
199 198
                    $this->addIndex($class, $index, $keys);
200
                }
201
            }
202
        }
203
204
        // Set shard key after all fields to ensure we mapped all its keys
205 899
        if (isset($classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey'])) {
206 62
            $this->setShardKey($class, $classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey']);
207
        }
208
209
        /** @var $method \ReflectionMethod */
210 898
        foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
211
            /* Filter for the declaring class only. Callbacks from parent
212
             * classes will already be registered.
213
             */
214 634
            if ($method->getDeclaringClass()->name !== $reflClass->name) {
215 314
                continue;
216
            }
217
218 634
            foreach ($this->reader->getMethodAnnotations($method) as $annot) {
219 307
                if ($annot instanceof ODM\AlsoLoad) {
220 13
                    $class->registerAlsoLoadMethod($method->getName(), $annot->value);
221
                }
222
223 307
                if ( ! isset($classAnnotations[ODM\HasLifecycleCallbacks::class])) {
224 72
                    continue;
225
                }
226
227 288
                if ($annot instanceof ODM\PrePersist) {
228 267
                    $class->addLifecycleCallback($method->getName(), Events::prePersist);
229 86
                } elseif ($annot instanceof ODM\PostPersist) {
230 11
                    $class->addLifecycleCallback($method->getName(), Events::postPersist);
231 85
                } elseif ($annot instanceof ODM\PreUpdate) {
232 15
                    $class->addLifecycleCallback($method->getName(), Events::preUpdate);
233 80
                } elseif ($annot instanceof ODM\PostUpdate) {
234 67
                    $class->addLifecycleCallback($method->getName(), Events::postUpdate);
235 74
                } elseif ($annot instanceof ODM\PreRemove) {
236 72
                    $class->addLifecycleCallback($method->getName(), Events::preRemove);
237 74
                } elseif ($annot instanceof ODM\PostRemove) {
238 70
                    $class->addLifecycleCallback($method->getName(), Events::postRemove);
239 74
                } elseif ($annot instanceof ODM\PreLoad) {
240 71
                    $class->addLifecycleCallback($method->getName(), Events::preLoad);
241 73
                } elseif ($annot instanceof ODM\PostLoad) {
242 72
                    $class->addLifecycleCallback($method->getName(), Events::postLoad);
243 11
                } elseif ($annot instanceof ODM\PreFlush) {
244 634
                    $class->addLifecycleCallback($method->getName(), Events::preFlush);
245
                }
246
            }
247
        }
248 898
    }
249
250 222
    private function addIndex(ClassMetadataInfo $class, $index, array $keys = array())
251
    {
252 222
        $keys = array_merge($keys, $index->keys);
253 222
        $options = array();
254 222
        $allowed = array('name', 'dropDups', 'background', 'safe', 'unique', 'sparse', 'expireAfterSeconds');
255 222
        foreach ($allowed as $name) {
256 222
            if (isset($index->$name)) {
257 222
                $options[$name] = $index->$name;
258
            }
259
        }
260 222
        if (! empty($index->partialFilterExpression)) {
261 3
            $options['partialFilterExpression'] = $index->partialFilterExpression;
262
        }
263 222
        $options = array_merge($options, $index->options);
264 222
        $class->addIndex($keys, $options);
265 222
    }
266
267
    /**
268
     * @param ClassMetadataInfo $class
269
     * @param ODM\ShardKey      $shardKey
270
     *
271
     * @throws MappingException
272
     */
273 62
    private function setShardKey(ClassMetadataInfo $class, ODM\ShardKey $shardKey)
274
    {
275 62
        $options = array();
276 62
        $allowed = array('unique', 'numInitialChunks');
277 62
        foreach ($allowed as $name) {
278 62
            if (isset($shardKey->$name)) {
279 1
                $options[$name] = $shardKey->$name;
280
            }
281
        }
282
283 62
        $class->setShardKey($shardKey->keys, $options);
284 61
    }
285
286
    /**
287
     * Factory method for the Annotation Driver
288
     *
289
     * @param array|string $paths
290
     * @param Reader $reader
291
     * @return AnnotationDriver
292
     */
293 1139
    public static function create($paths = array(), Reader $reader = null)
294
    {
295 1139
        if ($reader === null) {
296 1139
            $reader = new AnnotationReader();
297
        }
298 1139
        return new self($reader, $paths);
299
    }
300
}
301