Completed
Pull Request — master (#1906)
by Maciej
15:15
created

AnnotationDriver::isTransient()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 6
cts 6
cp 1
rs 9.9
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 3
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 ReflectionClass;
16
use ReflectionMethod;
17
use const E_USER_DEPRECATED;
18
use function array_merge;
19
use function array_replace;
20
use function constant;
21
use function get_class;
22
use function is_array;
23
use function trigger_error;
24
25
/**
26
 * The AnnotationDriver reads the mapping metadata from docblock annotations.
27
 */
28
class AnnotationDriver extends AbstractAnnotationDriver
29
{
30 918
    public function isTransient($className)
31
    {
32 918
        $classAnnotations = $this->reader->getClassAnnotations(new ReflectionClass($className));
33
34 918
        foreach ($classAnnotations as $annot) {
35 917
            if ($annot instanceof ODM\AbstractDocument) {
36 917
                return false;
37
            }
38
        }
39 69
        return true;
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45 1441
    public function loadMetadataForClass($className, \Doctrine\Common\Persistence\Mapping\ClassMetadata $class) : void
46
    {
47
        /** @var ClassMetadata $class */
48 1441
        $reflClass = $class->getReflectionClass();
49
50 1441
        $classAnnotations = $this->reader->getClassAnnotations($reflClass);
51
52 1441
        $documentAnnot = null;
53 1441
        foreach ($classAnnotations as $annot) {
54 1439
            $classAnnotations[get_class($annot)] = $annot;
55
56 1439
            if ($annot instanceof ODM\AbstractDocument) {
57 1439
                if ($documentAnnot !== null) {
58 5
                    throw MappingException::classCanOnlyBeMappedByOneAbstractDocument($className, $documentAnnot, $annot);
59
                }
60 1439
                $documentAnnot = $annot;
61
            }
62
63
            // non-document class annotations
64 1439
            if ($annot instanceof ODM\AbstractIndex) {
65 6
                $this->addIndex($class, $annot);
66
            }
67 1439
            if ($annot instanceof ODM\Indexes) {
68 94
                foreach (is_array($annot->value) ? $annot->value : [$annot->value] as $index) {
69 94
                    $this->addIndex($class, $index);
70
                }
71 1439
            } elseif ($annot instanceof ODM\InheritanceType) {
72 859
                $class->setInheritanceType(constant(ClassMetadata::class . '::INHERITANCE_TYPE_' . $annot->value));
73 1439
            } elseif ($annot instanceof ODM\DiscriminatorField) {
74 132
                $class->setDiscriminatorField($annot->value);
75 1439
            } elseif ($annot instanceof ODM\DiscriminatorMap) {
76 131
                $class->setDiscriminatorMap($annot->value);
77 1439
            } elseif ($annot instanceof ODM\DiscriminatorValue) {
78
                $class->setDiscriminatorValue($annot->value);
79 1439
            } elseif ($annot instanceof ODM\ChangeTrackingPolicy) {
80 66
                $class->setChangeTrackingPolicy(constant(ClassMetadata::class . '::CHANGETRACKING_' . $annot->value));
81 1439
            } elseif ($annot instanceof ODM\DefaultDiscriminatorValue) {
82 68
                $class->setDefaultDiscriminatorValue($annot->value);
83 1439
            } elseif ($annot instanceof ODM\ReadPreference) {
84 1439
                $class->setReadPreference($annot->value, $annot->tags);
85
            }
86
        }
87
88 1436
        if ($documentAnnot === null) {
89 3
            throw MappingException::classIsNotAValidDocument($className);
90
        }
91
92 1434
        if ($documentAnnot instanceof ODM\MappedSuperclass) {
93 849
            $class->isMappedSuperclass = true;
94 1433
        } elseif ($documentAnnot instanceof ODM\EmbeddedDocument) {
95 280
            $class->isEmbeddedDocument = true;
96 1426
        } elseif ($documentAnnot instanceof ODM\QueryResultDocument) {
97 67
            $class->isQueryResultDocument = true;
98 1426
        } elseif ($documentAnnot instanceof ODM\File) {
99 79
            $class->isFile = true;
100
101 79
            if ($documentAnnot->chunkSizeBytes !== null) {
102 78
                $class->setChunkSizeBytes($documentAnnot->chunkSizeBytes);
103
            }
104
        }
105
106 1434
        if (isset($documentAnnot->db)) {
107 1
            $class->setDatabase($documentAnnot->db);
108
        }
109 1434
        if (isset($documentAnnot->collection)) {
110 904
            $class->setCollection($documentAnnot->collection);
111
        }
112
        // Store bucketName as collection name for GridFS files
113 1434
        if (isset($documentAnnot->bucketName)) {
114 1
            $class->setBucketName($documentAnnot->bucketName);
115
        }
116 1434
        if (isset($documentAnnot->repositoryClass)) {
117 77
            $class->setCustomRepositoryClass($documentAnnot->repositoryClass);
118
        }
119 1434
        if (isset($documentAnnot->writeConcern)) {
120 10
            $class->setWriteConcern($documentAnnot->writeConcern);
121
        }
122 1434
        if (isset($documentAnnot->indexes)) {
123 1433
            foreach ($documentAnnot->indexes as $index) {
124
                $this->addIndex($class, $index);
125
            }
126
        }
127 1434
        if (! empty($documentAnnot->readOnly)) {
128 5
            $class->markReadOnly();
129
        }
130
131 1434
        foreach ($reflClass->getProperties() as $property) {
132 1433
            if (($class->isMappedSuperclass && ! $property->isPrivate())
133
                ||
134 1433
                ($class->isInheritedField($property->name) && $property->getDeclaringClass()->name !== $class->name)) {
135 891
                continue;
136
            }
137
138 1432
            $indexes    = [];
139 1432
            $mapping    = ['fieldName' => $property->getName()];
140 1432
            $fieldAnnot = null;
141
142 1432
            foreach ($this->reader->getPropertyAnnotations($property) as $annot) {
143 1432
                if ($annot instanceof ODM\AbstractField) {
144 1432
                    $fieldAnnot = $annot;
145 1432
                    if ($annot->isDeprecated()) {
146
                        @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...
147
                    }
148
                }
149 1432
                if ($annot instanceof ODM\AbstractIndex) {
150 182
                    $indexes[] = $annot;
151
                }
152 1432
                if ($annot instanceof ODM\Indexes) {
153
                    foreach (is_array($annot->value) ? $annot->value : [$annot->value] as $index) {
154
                        $indexes[] = $index;
155
                    }
156 1432
                } elseif ($annot instanceof ODM\AlsoLoad) {
157 15
                    $mapping['alsoLoadFields'] = (array) $annot->value;
158 1432
                } elseif ($annot instanceof ODM\Version) {
159 108
                    $mapping['version'] = true;
160 1432
                } elseif ($annot instanceof ODM\Lock) {
161 1432
                    $mapping['lock'] = true;
162
                }
163
            }
164
165 1432
            if ($fieldAnnot) {
166 1432
                $mapping = array_replace($mapping, (array) $fieldAnnot);
167 1432
                $class->mapField($mapping);
168
            }
169
170 1432
            if (! $indexes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $indexes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
171 1432
                continue;
172
            }
173
174 182
            foreach ($indexes as $index) {
175 182
                $name = $mapping['name'] ?? $mapping['fieldName'];
176 182
                $keys = [$name => $index->order ?: 'asc'];
177 182
                $this->addIndex($class, $index, $keys);
178
            }
179
        }
180
181
        // Set shard key after all fields to ensure we mapped all its keys
182 1431
        if (isset($classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey'])) {
183 88
            $this->setShardKey($class, $classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey']);
184
        }
185
186
        /** @var ReflectionMethod $method */
187 1430
        foreach ($reflClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
188
            /* Filter for the declaring class only. Callbacks from parent
189
             * classes will already be registered.
190
             */
191 1178
            if ($method->getDeclaringClass()->name !== $reflClass->name) {
192 856
                continue;
193
            }
194
195 1178
            foreach ($this->reader->getMethodAnnotations($method) as $annot) {
196 850
                if ($annot instanceof ODM\AlsoLoad) {
197 13
                    $class->registerAlsoLoadMethod($method->getName(), $annot->value);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
198
                }
199
200 850
                if (! isset($classAnnotations[ODM\HasLifecycleCallbacks::class])) {
201 84
                    continue;
202
                }
203
204 831
                if ($annot instanceof ODM\PrePersist) {
205 812
                    $class->addLifecycleCallback($method->getName(), Events::prePersist);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
206 95
                } elseif ($annot instanceof ODM\PostPersist) {
207 10
                    $class->addLifecycleCallback($method->getName(), Events::postPersist);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
208 95
                } elseif ($annot instanceof ODM\PreUpdate) {
209 15
                    $class->addLifecycleCallback($method->getName(), Events::preUpdate);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
210 90
                } elseif ($annot instanceof ODM\PostUpdate) {
211 79
                    $class->addLifecycleCallback($method->getName(), Events::postUpdate);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
212 84
                } elseif ($annot instanceof ODM\PreRemove) {
213 82
                    $class->addLifecycleCallback($method->getName(), Events::preRemove);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
214 84
                } elseif ($annot instanceof ODM\PostRemove) {
215 82
                    $class->addLifecycleCallback($method->getName(), Events::postRemove);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
216 84
                } elseif ($annot instanceof ODM\PreLoad) {
217 83
                    $class->addLifecycleCallback($method->getName(), Events::preLoad);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
218 83
                } elseif ($annot instanceof ODM\PostLoad) {
219 82
                    $class->addLifecycleCallback($method->getName(), Events::postLoad);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
220 11
                } elseif ($annot instanceof ODM\PreFlush) {
221 1178
                    $class->addLifecycleCallback($method->getName(), Events::preFlush);
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
222
                }
223
            }
224
        }
225 1430
    }
226
227 214
    private function addIndex(ClassMetadata $class, AbstractIndex $index, array $keys = []) : void
228
    {
229 214
        $keys    = array_merge($keys, $index->keys);
230 214
        $options = [];
231 214
        $allowed = ['name', 'dropDups', 'background', 'unique', 'sparse', 'expireAfterSeconds'];
232 214
        foreach ($allowed as $name) {
233 214
            if (! isset($index->$name)) {
234 214
                continue;
235
            }
236
237 214
            $options[$name] = $index->$name;
238
        }
239 214
        if (! empty($index->partialFilterExpression)) {
240 2
            $options['partialFilterExpression'] = $index->partialFilterExpression;
241
        }
242 214
        $options = array_merge($options, $index->options);
243 214
        $class->addIndex($keys, $options);
244 214
    }
245
246
    /**
247
     * @throws MappingException
248
     */
249 88
    private function setShardKey(ClassMetadata $class, ODM\ShardKey $shardKey) : void
250
    {
251 88
        $options = [];
252 88
        $allowed = ['unique', 'numInitialChunks'];
253 88
        foreach ($allowed as $name) {
254 88
            if (! isset($shardKey->$name)) {
255 88
                continue;
256
            }
257
258
            $options[$name] = $shardKey->$name;
259
        }
260
261 88
        $class->setShardKey($shardKey->keys, $options);
262 87
    }
263
264
    /**
265
     * Factory method for the Annotation Driver
266
     *
267
     * @param array|string $paths
268
     */
269 1704
    public static function create($paths = [], ?Reader $reader = null) : AnnotationDriver
270
    {
271 1704
        if ($reader === null) {
272 1704
            $reader = new AnnotationReader();
273
        }
274 1704
        return new self($reader, $paths);
275
    }
276
}
277