Completed
Push — master ( 056d89...fc4c80 )
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\ClassMetadata;
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\ClassMetadata as MappingClassMetadata;
13
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo;
14
use Doctrine\ODM\MongoDB\Mapping\MappingException;
15
16
/**
17
 * The AnnotationDriver reads the mapping metadata from docblock annotations.
18
 *
19
 * @since       1.0
20
 */
21
class AnnotationDriver extends AbstractAnnotationDriver
22
{
23
    protected $entityAnnotationClasses = array(
24
        ODM\Document::class            => 1,
25
        ODM\MappedSuperclass::class    => 2,
26
        ODM\EmbeddedDocument::class    => 3,
27
        ODM\QueryResultDocument::class => 4,
28
    );
29
30
    /**
31
     * {@inheritdoc}
32
     */
33 1365
    public function loadMetadataForClass($className, ClassMetadata $class)
34
    {
35
        /** @var $class ClassMetadataInfo */
36 1365
        $reflClass = $class->getReflectionClass();
37
38 1365
        $classAnnotations = $this->reader->getClassAnnotations($reflClass);
39
40 1365
        $documentAnnots = array();
41 1365
        foreach ($classAnnotations as $annot) {
42 1363
            $classAnnotations[get_class($annot)] = $annot;
43
44 1363
            foreach ($this->entityAnnotationClasses as $annotClass => $i) {
45 1363
                if ($annot instanceof $annotClass) {
46 1363
                    $documentAnnots[$i] = $annot;
47 1363
                    continue 2;
48
                }
49
            }
50
51
            // non-document class annotations
52 909
            if ($annot instanceof ODM\AbstractIndex) {
53 6
                $this->addIndex($class, $annot);
54
            }
55 909
            if ($annot instanceof ODM\Indexes) {
56 61
                foreach (is_array($annot->value) ? $annot->value : array($annot->value) as $index) {
57 61
                    $this->addIndex($class, $index);
58
                }
59 894
            } elseif ($annot instanceof ODM\InheritanceType) {
60 833
                $class->setInheritanceType(constant(MappingClassMetadata::class . '::INHERITANCE_TYPE_'.$annot->value));
61 892
            } elseif ($annot instanceof ODM\DiscriminatorField) {
62 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...
63 891
            } elseif ($annot instanceof ODM\DiscriminatorMap) {
64 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...
65 830
            } elseif ($annot instanceof ODM\DiscriminatorValue) {
66
                $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...
67 830
            } elseif ($annot instanceof ODM\ChangeTrackingPolicy) {
68 46
                $class->setChangeTrackingPolicy(constant(MappingClassMetadata::class . '::CHANGETRACKING_'.$annot->value));
69 828
            } elseif ($annot instanceof ODM\DefaultDiscriminatorValue) {
70 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...
71 823
            } elseif ($annot instanceof ODM\ReadPreference) {
72 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...fo::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...
73
            }
74
75
        }
76
77 1365
        if ( ! $documentAnnots) {
78 3
            throw MappingException::classIsNotAValidDocument($className);
79
        }
80
81
        // find the winning document annotation
82 1363
        ksort($documentAnnots);
83 1363
        $documentAnnot = reset($documentAnnots);
84
85 1363
        if ($documentAnnot instanceof ODM\MappedSuperclass) {
86 822
            $class->isMappedSuperclass = true;
87 1362
        } elseif ($documentAnnot instanceof ODM\EmbeddedDocument) {
88 234
            $class->isEmbeddedDocument = true;
89 1355
        } elseif ($documentAnnot instanceof ODM\QueryResultDocument) {
90 47
            $class->isQueryResultDocument = true;
91
        }
92 1363
        if (isset($documentAnnot->db)) {
93 1
            $class->setDatabase($documentAnnot->db);
94
        }
95 1363
        if (isset($documentAnnot->collection)) {
96 867
            $class->setCollection($documentAnnot->collection);
97
        }
98 1363
        if (isset($documentAnnot->repositoryClass)) {
99 57
            $class->setCustomRepositoryClass($documentAnnot->repositoryClass);
100
        }
101 1363
        if (isset($documentAnnot->writeConcern)) {
102 10
            $class->setWriteConcern($documentAnnot->writeConcern);
103
        }
104 1363
        if (isset($documentAnnot->indexes)) {
105 1362
            foreach ($documentAnnot->indexes as $index) {
106
                $this->addIndex($class, $index);
107
            }
108
        }
109 1363
        if (! empty($documentAnnot->readOnly)) {
110 5
            $class->markReadOnly();
111
        }
112
113 1363
        foreach ($reflClass->getProperties() as $property) {
114 1362
            if (($class->isMappedSuperclass && ! $property->isPrivate())
115
                ||
116 1362
                ($class->isInheritedField($property->name) && $property->getDeclaringClass()->name !== $class->name)) {
117 864
                continue;
118
            }
119
120 1361
            $indexes = array();
121 1361
            $mapping = array('fieldName' => $property->getName());
122 1361
            $fieldAnnot = null;
123
124 1361
            foreach ($this->reader->getPropertyAnnotations($property) as $annot) {
125 1361
                if ($annot instanceof ODM\AbstractField) {
126 1361
                    $fieldAnnot = $annot;
127 1361
                    if ($annot->isDeprecated()) {
128
                        @trigger_error($annot->getDeprecationMessage(), E_USER_DEPRECATED);
129
                    }
130
                }
131 1361
                if ($annot instanceof ODM\AbstractIndex) {
132 159
                    $indexes[] = $annot;
133
                }
134 1361
                if ($annot instanceof ODM\Indexes) {
135
                    foreach (is_array($annot->value) ? $annot->value : array($annot->value) as $index) {
136
                        $indexes[] = $index;
137
                    }
138 1361
                } elseif ($annot instanceof ODM\AlsoLoad) {
139 15
                    $mapping['alsoLoadFields'] = (array) $annot->value;
140 1361
                } elseif ($annot instanceof ODM\Version) {
141 67
                    $mapping['version'] = true;
142 1361
                } elseif ($annot instanceof ODM\Lock) {
143 1361
                    $mapping['lock'] = true;
144
                }
145
            }
146
147 1361
            if ($fieldAnnot) {
148 1361
                $mapping = array_replace($mapping, (array) $fieldAnnot);
149 1361
                $class->mapField($mapping);
150
            }
151
152 1361
            if ($indexes) {
153 159
                foreach ($indexes as $index) {
154 159
                    $name = $mapping['name'] ?? $mapping['fieldName'];
155 159
                    $keys = array($name => $index->order ?: 'asc');
156 1361
                    $this->addIndex($class, $index, $keys);
157
                }
158
            }
159
        }
160
161
        // Set shard key after all fields to ensure we mapped all its keys
162 1361
        if (isset($classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey'])) {
163 59
            $this->setShardKey($class, $classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey']);
164
        }
165
166
        /** @var $method \ReflectionMethod */
167 1360
        foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
168
            /* Filter for the declaring class only. Callbacks from parent
169
             * classes will already be registered.
170
             */
171 1125
            if ($method->getDeclaringClass()->name !== $reflClass->name) {
172 830
                continue;
173
            }
174
175 1125
            foreach ($this->reader->getMethodAnnotations($method) as $annot) {
176 820
                if ($annot instanceof ODM\AlsoLoad) {
177 13
                    $class->registerAlsoLoadMethod($method->getName(), $annot->value);
178
                }
179
180 820
                if ( ! isset($classAnnotations[ODM\HasLifecycleCallbacks::class])) {
181 64
                    continue;
182
                }
183
184 801
                if ($annot instanceof ODM\PrePersist) {
185 786
                    $class->addLifecycleCallback($method->getName(), Events::prePersist);
186 71
                } elseif ($annot instanceof ODM\PostPersist) {
187 10
                    $class->addLifecycleCallback($method->getName(), Events::postPersist);
188 71
                } elseif ($annot instanceof ODM\PreUpdate) {
189 15
                    $class->addLifecycleCallback($method->getName(), Events::preUpdate);
190 66
                } elseif ($annot instanceof ODM\PostUpdate) {
191 55
                    $class->addLifecycleCallback($method->getName(), Events::postUpdate);
192 64
                } elseif ($annot instanceof ODM\PreRemove) {
193 62
                    $class->addLifecycleCallback($method->getName(), Events::preRemove);
194 64
                } elseif ($annot instanceof ODM\PostRemove) {
195 62
                    $class->addLifecycleCallback($method->getName(), Events::postRemove);
196 64
                } elseif ($annot instanceof ODM\PreLoad) {
197 63
                    $class->addLifecycleCallback($method->getName(), Events::preLoad);
198 63
                } elseif ($annot instanceof ODM\PostLoad) {
199 62
                    $class->addLifecycleCallback($method->getName(), Events::postLoad);
200 11
                } elseif ($annot instanceof ODM\PreFlush) {
201 1125
                    $class->addLifecycleCallback($method->getName(), Events::preFlush);
202
                }
203
            }
204
        }
205 1360
    }
206
207 180
    private function addIndex(ClassMetadataInfo $class, $index, array $keys = array())
208
    {
209 180
        $keys = array_merge($keys, $index->keys);
210 180
        $options = array();
211 180
        $allowed = array('name', 'dropDups', 'background', 'unique', 'sparse', 'expireAfterSeconds');
212 180
        foreach ($allowed as $name) {
213 180
            if (isset($index->$name)) {
214 180
                $options[$name] = $index->$name;
215
            }
216
        }
217 180
        if (! empty($index->partialFilterExpression)) {
218 2
            $options['partialFilterExpression'] = $index->partialFilterExpression;
219
        }
220 180
        $options = array_merge($options, $index->options);
221 180
        $class->addIndex($keys, $options);
222 180
    }
223
224
    /**
225
     * @param ClassMetadataInfo $class
226
     * @param ODM\ShardKey      $shardKey
227
     *
228
     * @throws MappingException
229
     */
230 59
    private function setShardKey(ClassMetadataInfo $class, ODM\ShardKey $shardKey)
231
    {
232 59
        $options = array();
233 59
        $allowed = array('unique', 'numInitialChunks');
234 59
        foreach ($allowed as $name) {
235 59
            if (isset($shardKey->$name)) {
236 59
                $options[$name] = $shardKey->$name;
237
            }
238
        }
239
240 59
        $class->setShardKey($shardKey->keys, $options);
241 58
    }
242
243
    /**
244
     * Factory method for the Annotation Driver
245
     *
246
     * @param array|string $paths
247
     * @param Reader $reader
248
     * @return AnnotationDriver
249
     */
250 1618
    public static function create($paths = array(), Reader $reader = null)
251
    {
252 1618
        if ($reader === null) {
253 1618
            $reader = new AnnotationReader();
254
        }
255 1618
        return new self($reader, $paths);
256
    }
257
}
258