Completed
Push — master ( fc4c80...420611 )
by Maciej
10s
created

ODM/MongoDB/Mapping/Driver/AnnotationDriver.php (5 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);
254
    }
255
}
256