Completed
Push — master ( 26ecbc...8c0c5d )
by Maciej
14s
created

ODM/MongoDB/Mapping/ClassMetadataFactory.php (2 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
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB\Mapping;
6
7
use Doctrine\Common\EventManager;
8
use Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory;
9
use Doctrine\Common\Persistence\Mapping\ClassMetadata as ClassMetadataInterface;
10
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
11
use Doctrine\Common\Persistence\Mapping\ReflectionService;
12
use Doctrine\ODM\MongoDB\Configuration;
13
use Doctrine\ODM\MongoDB\ConfigurationException;
14
use Doctrine\ODM\MongoDB\DocumentManager;
15
use Doctrine\ODM\MongoDB\Event\LoadClassMetadataEventArgs;
16
use Doctrine\ODM\MongoDB\Events;
17
use Doctrine\ODM\MongoDB\Id\AbstractIdGenerator;
18
use Doctrine\ODM\MongoDB\Id\AlnumGenerator;
19
use Doctrine\ODM\MongoDB\Id\AutoGenerator;
20
use Doctrine\ODM\MongoDB\Id\IncrementGenerator;
21
use Doctrine\ODM\MongoDB\Id\UuidGenerator;
22
use ReflectionException;
23
use function assert;
24
use function get_class;
25
use function get_class_methods;
26
use function in_array;
27
use function ucfirst;
28
29
/**
30
 * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
31
 * metadata mapping informations of a class which describes how a class should be mapped
32
 * to a document database.
33
 */
34
class ClassMetadataFactory extends AbstractClassMetadataFactory
35
{
36
    /** @var string */
37
    protected $cacheSalt = '$MONGODBODMCLASSMETADATA';
38
39
    /** @var DocumentManager The DocumentManager instance */
40
    private $dm;
41
42
    /** @var Configuration The Configuration instance */
43
    private $config;
44
45
    /** @var MappingDriver The used metadata driver. */
46
    private $driver;
47
48
    /** @var EventManager The event manager instance */
49
    private $evm;
50
51 1712
    public function setDocumentManager(DocumentManager $dm) : void
52
    {
53 1712
        $this->dm = $dm;
54 1712
    }
55
56 1712
    public function setConfiguration(Configuration $config) : void
57
    {
58 1712
        $this->config = $config;
59 1712
    }
60
61 1440
    public function getMetadataFor($className)
62
    {
63 1440
        return parent::getMetadataFor($this->dm->getClassNameResolver()->getRealClass($className));
64
    }
65
66
    /**
67
     * Lazy initialization of this stuff, especially the metadata driver,
68
     * since these are not needed at all when a metadata cache is active.
69
     */
70 1439
    protected function initialize() : void
71
    {
72 1439
        $driver = $this->config->getMetadataDriverImpl();
73 1439
        if ($driver === null) {
74
            throw ConfigurationException::noMetadataDriverConfigured();
75
        }
76
77 1439
        $this->driver      = $driver;
78 1439
        $this->evm         = $this->dm->getEventManager();
79 1439
        $this->initialized = true;
80 1439
    }
81
82
    /**
83
     * {@inheritDoc}
84
     */
85
    protected function getFqcnFromAlias($namespaceAlias, $simpleClassName) : string
86
    {
87
        return $this->config->getDocumentNamespace($namespaceAlias) . '\\' . $simpleClassName;
88
    }
89
90
    /**
91
     * {@inheritDoc}
92
     */
93 915
    protected function getDriver()
94
    {
95 915
        return $this->driver;
96
    }
97
98
    /**
99
     * {@inheritDoc}
100
     */
101 1435
    protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService) : void
102
    {
103 1435
    }
104
105
    /**
106
     * {@inheritDoc}
107
     */
108 1439
    protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService) : void
109
    {
110 1439
    }
111
112
    /**
113
     * {@inheritDoc}
114
     */
115 1435
    protected function isEntity(ClassMetadataInterface $class) : bool
116
    {
117 1435
        assert($class instanceof ClassMetadata);
118 1435
        return ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument && ! $class->isQueryResultDocument;
119
    }
120
121
    /**
122
     * {@inheritDoc}
123
     */
124 1439
    protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents = []) : void
125
    {
126 1439
        assert($class instanceof ClassMetadata);
127 1439
        if ($parent instanceof ClassMetadata) {
128 913
            $class->setInheritanceType($parent->inheritanceType);
129 913
            $class->setDiscriminatorField($parent->discriminatorField);
130 913
            $class->setDiscriminatorMap($parent->discriminatorMap);
131 913
            $class->setDefaultDiscriminatorValue($parent->defaultDiscriminatorValue);
132 913
            $class->setIdGeneratorType($parent->generatorType);
133 913
            $this->addInheritedFields($class, $parent);
134 913
            $this->addInheritedRelations($class, $parent);
135 913
            $this->addInheritedIndexes($class, $parent);
136 913
            $this->setInheritedShardKey($class, $parent);
137 913
            $class->setIdentifier($parent->identifier);
138 913
            $class->setVersioned($parent->isVersioned);
139 913
            $class->setVersionField($parent->versionField);
140 913
            $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
141 913
            $class->setAlsoLoadMethods($parent->alsoLoadMethods);
142 913
            $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
143 913
            $class->setReadPreference($parent->readPreference, $parent->readPreferenceTags);
144 913
            $class->setWriteConcern($parent->writeConcern);
145
146 913
            if ($parent->isMappedSuperclass) {
147 849
                $class->setCustomRepositoryClass($parent->customRepositoryClassName);
148
            }
149
150 913
            if ($parent->isFile) {
151 1
                $class->isFile = true;
152 1
                $class->setBucketName($parent->bucketName);
153
154 1
                if ($parent->chunkSizeBytes !== null) {
155 1
                    $class->setChunkSizeBytes($parent->chunkSizeBytes);
156
                }
157
            }
158
        }
159
160
        // Invoke driver
161
        try {
162 1439
            $this->driver->loadMetadataForClass($class->getName(), $class);
0 ignored issues
show
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
163 6
        } catch (ReflectionException $e) {
164
            throw MappingException::reflectionFailure($class->getName(), $e);
0 ignored issues
show
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
165
        }
166
167 1435
        $this->validateIdentifier($class);
168
169 1435
        if ($parent instanceof ClassMetadata && $rootEntityFound && $parent->generatorType === $class->generatorType) {
170 121
            if ($parent->generatorType) {
171 121
                $class->setIdGeneratorType($parent->generatorType);
172
            }
173 121
            if ($parent->generatorOptions) {
174
                $class->setIdGeneratorOptions($parent->generatorOptions);
175
            }
176 121
            if ($parent->idGenerator) {
177 121
                $class->setIdGenerator($parent->idGenerator);
178
            }
179
        } else {
180 1435
            $this->completeIdGeneratorMapping($class);
181
        }
182
183 1435
        if ($parent instanceof ClassMetadata && $parent->isInheritanceTypeSingleCollection()) {
184 107
            $class->setDatabase($parent->getDatabase());
185 107
            $class->setCollection($parent->getCollection());
186
        }
187
188 1435
        $class->setParentClasses($nonSuperclassParents);
189
190 1435
        if (! $this->evm->hasListeners(Events::loadClassMetadata)) {
191 1433
            return;
192
        }
193
194 2
        $eventArgs = new LoadClassMetadataEventArgs($class, $this->dm);
195 2
        $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
196 2
    }
197
198
    /**
199
     * Validates the identifier mapping.
200
     *
201
     * @throws MappingException
202
     */
203 1435
    protected function validateIdentifier(ClassMetadata $class) : void
204
    {
205 1435
        if (! $class->identifier && ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument && ! $class->isQueryResultDocument) {
206
            throw MappingException::identifierRequired($class->name);
207
        }
208 1435
    }
209
210
    /**
211
     * {@inheritdoc}
212
     */
213 1439
    protected function newClassMetadataInstance($className) : ClassMetadata
214
    {
215 1439
        return new ClassMetadata($className);
216
    }
217
218 1435
    private function completeIdGeneratorMapping(ClassMetadata $class) : void
219
    {
220 1435
        $idGenOptions = $class->generatorOptions;
221 1435
        switch ($class->generatorType) {
222
            case ClassMetadata::GENERATOR_TYPE_AUTO:
223 1367
                $class->setIdGenerator(new AutoGenerator());
224 1367
                break;
225 View Code Duplication
            case ClassMetadata::GENERATOR_TYPE_INCREMENT:
226 9
                $incrementGenerator = new IncrementGenerator();
227 9
                if (isset($idGenOptions['key'])) {
228
                    $incrementGenerator->setKey((string) $idGenOptions['key']);
229
                }
230 9
                if (isset($idGenOptions['collection'])) {
231
                    $incrementGenerator->setCollection((string) $idGenOptions['collection']);
232
                }
233 9
                if (isset($idGenOptions['startingId'])) {
234 1
                    $incrementGenerator->setStartingId((int) $idGenOptions['startingId']);
235
                }
236 9
                $class->setIdGenerator($incrementGenerator);
237 9
                break;
238
            case ClassMetadata::GENERATOR_TYPE_UUID:
239 4
                $uuidGenerator = new UuidGenerator();
240 4
                if (isset($idGenOptions['salt'])) {
241 2
                    $uuidGenerator->setSalt((string) $idGenOptions['salt']);
242
                }
243 4
                $class->setIdGenerator($uuidGenerator);
244 4
                break;
245 View Code Duplication
            case ClassMetadata::GENERATOR_TYPE_ALNUM:
246 1
                $alnumGenerator = new AlnumGenerator();
247 1
                if (isset($idGenOptions['pad'])) {
248
                    $alnumGenerator->setPad((int) $idGenOptions['pad']);
249
                }
250 1
                if (isset($idGenOptions['chars'])) {
251 1
                    $alnumGenerator->setChars((string) $idGenOptions['chars']);
252
                } elseif (isset($idGenOptions['awkwardSafe'])) {
253
                    $alnumGenerator->setAwkwardSafeMode((bool) $idGenOptions['awkwardSafe']);
254
                }
255
256 1
                $class->setIdGenerator($alnumGenerator);
257 1
                break;
258
            case ClassMetadata::GENERATOR_TYPE_CUSTOM:
259
                if (empty($idGenOptions['class'])) {
260
                    throw MappingException::missingIdGeneratorClass($class->name);
261
                }
262
263
                $customGenerator = new $idGenOptions['class']();
264
                unset($idGenOptions['class']);
265
                if (! $customGenerator instanceof AbstractIdGenerator) {
266
                    throw MappingException::classIsNotAValidGenerator(get_class($customGenerator));
267
                }
268
269
                $methods = get_class_methods($customGenerator);
270
                foreach ($idGenOptions as $name => $value) {
271
                    $method = 'set' . ucfirst($name);
272
                    if (! in_array($method, $methods)) {
273
                        throw MappingException::missingGeneratorSetter(get_class($customGenerator), $name);
274
                    }
275
276
                    $customGenerator->$method($value);
277
                }
278
                $class->setIdGenerator($customGenerator);
279
                break;
280
            case ClassMetadata::GENERATOR_TYPE_NONE:
281 150
                break;
282
            default:
283
                throw new MappingException('Unknown generator type: ' . $class->generatorType);
284
        }
285 1435
    }
286
287
    /**
288
     * Adds inherited fields to the subclass mapping.
289
     */
290 913
    private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass) : void
291
    {
292 913
        foreach ($parentClass->fieldMappings as $fieldName => $mapping) {
293 139 View Code Duplication
            if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
294 133
                $mapping['inherited'] = $parentClass->name;
295
            }
296 139
            if (! isset($mapping['declared'])) {
297 139
                $mapping['declared'] = $parentClass->name;
298
            }
299 139
            $subClass->addInheritedFieldMapping($mapping);
300
        }
301 913
        foreach ($parentClass->reflFields as $name => $field) {
302 139
            $subClass->reflFields[$name] = $field;
303
        }
304 913
    }
305
306
307
    /**
308
     * Adds inherited association mappings to the subclass mapping.
309
     *
310
     * @throws MappingException
311
     */
312 913
    private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass) : void
313
    {
314 913
        foreach ($parentClass->associationMappings as $field => $mapping) {
315 89
            if ($parentClass->isMappedSuperclass) {
316 4
                $mapping['sourceDocument'] = $subClass->name;
317
            }
318
319 89 View Code Duplication
            if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
320 85
                $mapping['inherited'] = $parentClass->name;
321
            }
322 89
            if (! isset($mapping['declared'])) {
323 89
                $mapping['declared'] = $parentClass->name;
324
            }
325 89
            $subClass->addInheritedAssociationMapping($mapping);
326
        }
327 913
    }
328
329
    /**
330
     * Adds inherited indexes to the subclass mapping.
331
     */
332 913
    private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass) : void
333
    {
334 913
        foreach ($parentClass->indexes as $index) {
335 65
            $subClass->addIndex($index['keys'], $index['options']);
336
        }
337 913
    }
338
339
    /**
340
     * Adds inherited shard key to the subclass mapping.
341
     */
342 913
    private function setInheritedShardKey(ClassMetadata $subClass, ClassMetadata $parentClass) : void
343
    {
344 913
        if (! $parentClass->isSharded()) {
345 908
            return;
346
        }
347
348 5
        $subClass->setShardKey(
349 5
            $parentClass->shardKey['keys'],
350 5
            $parentClass->shardKey['options']
351
        );
352 5
    }
353
}
354