ClassMetadataFactory::doLoadMetadata()   D
last analyzed

Complexity

Conditions 15
Paths 259

Size

Total Lines 73

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 46
CRAP Score 15.0163

Importance

Changes 0
Metric Value
dl 0
loc 73
ccs 46
cts 48
cp 0.9583
rs 4.0657
c 0
b 0
f 0
cc 15
nc 259
nop 4
crap 15.0163

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB\Mapping;
6
7
use Doctrine\Common\EventManager;
8
use Doctrine\ODM\MongoDB\Configuration;
9
use Doctrine\ODM\MongoDB\ConfigurationException;
10
use Doctrine\ODM\MongoDB\DocumentManager;
11
use Doctrine\ODM\MongoDB\Event\LoadClassMetadataEventArgs;
12
use Doctrine\ODM\MongoDB\Event\OnClassMetadataNotFoundEventArgs;
13
use Doctrine\ODM\MongoDB\Events;
14
use Doctrine\ODM\MongoDB\Id\AlnumGenerator;
15
use Doctrine\ODM\MongoDB\Id\AutoGenerator;
16
use Doctrine\ODM\MongoDB\Id\IdGenerator;
17
use Doctrine\ODM\MongoDB\Id\IncrementGenerator;
18
use Doctrine\ODM\MongoDB\Id\UuidGenerator;
19
use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory;
20
use Doctrine\Persistence\Mapping\ClassMetadata as ClassMetadataInterface;
21
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
22
use Doctrine\Persistence\Mapping\ReflectionService;
23
use ReflectionException;
24
use function assert;
25
use function get_class;
26
use function get_class_methods;
27
use function in_array;
28
use function interface_exists;
29
use function ucfirst;
30
31
/**
32
 * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
33
 * metadata mapping informations of a class which describes how a class should be mapped
34
 * to a document database.
35
 *
36
 * @internal
37
 */
38
final class ClassMetadataFactory extends AbstractClassMetadataFactory
39
{
40
    /** @var string */
41
    protected $cacheSalt = '$MONGODBODMCLASSMETADATA';
42
43
    /** @var DocumentManager The DocumentManager instance */
44
    private $dm;
45
46
    /** @var Configuration The Configuration instance */
47
    private $config;
48
49
    /** @var MappingDriver The used metadata driver. */
50
    private $driver;
51
52
    /** @var EventManager The event manager instance */
53
    private $evm;
54
55 1832
    public function setDocumentManager(DocumentManager $dm) : void
56
    {
57 1832
        $this->dm = $dm;
58 1832
    }
59
60 1832
    public function setConfiguration(Configuration $config) : void
61
    {
62 1832
        $this->config = $config;
63 1832
    }
64
65 1607
    public function getMetadataFor($className)
66
    {
67 1607
        return parent::getMetadataFor($this->dm->getClassNameResolver()->getRealClass($className));
68
    }
69
70
    /**
71
     * Lazy initialization of this stuff, especially the metadata driver,
72
     * since these are not needed at all when a metadata cache is active.
73
     */
74 1606
    protected function initialize() : void
75
    {
76 1606
        $driver = $this->config->getMetadataDriverImpl();
77 1606
        if ($driver === null) {
78
            throw ConfigurationException::noMetadataDriverConfigured();
79
        }
80
81 1606
        $this->driver      = $driver;
82 1606
        $this->evm         = $this->dm->getEventManager();
83 1606
        $this->initialized = true;
84 1606
    }
85
86
    /**
87
     * {@inheritDoc}
88
     */
89 21
    protected function onNotFoundMetadata($className)
90
    {
91 21
        if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {
92 19
            return null;
93
        }
94
95 2
        $eventArgs = new OnClassMetadataNotFoundEventArgs($className, $this->dm);
96
97 2
        $this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs);
98
99 2
        return $eventArgs->getFoundMetadata();
100
    }
101
102
    /**
103
     * {@inheritDoc}
104
     */
105
    protected function getFqcnFromAlias($namespaceAlias, $simpleClassName) : string
106
    {
107
        return $this->config->getDocumentNamespace($namespaceAlias) . '\\' . $simpleClassName;
108
    }
109
110
    /**
111
     * {@inheritDoc}
112
     */
113 1042
    protected function getDriver()
114
    {
115 1042
        return $this->driver;
116
    }
117
118
    /**
119
     * {@inheritDoc}
120
     */
121 1598
    protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService) : void
122
    {
123 1598
    }
124
125
    /**
126
     * {@inheritDoc}
127
     */
128 1606
    protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService) : void
129
    {
130 1606
    }
131
132
    /**
133
     * {@inheritDoc}
134
     */
135 1598
    protected function isEntity(ClassMetadataInterface $class) : bool
136
    {
137 1598
        assert($class instanceof ClassMetadata);
138
139 1598
        return ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument && ! $class->isQueryResultDocument && ! $class->isView();
140
    }
141
142
    /**
143
     * {@inheritDoc}
144
     */
145 1606
    protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents = []) : void
146
    {
147 1606
        assert($class instanceof ClassMetadata);
148 1606
        if ($parent instanceof ClassMetadata) {
149 1037
            $class->setInheritanceType($parent->inheritanceType);
150 1037
            $class->setDiscriminatorField($parent->discriminatorField);
151 1037
            $class->setDiscriminatorMap($parent->discriminatorMap);
152 1037
            $class->setDefaultDiscriminatorValue($parent->defaultDiscriminatorValue);
153 1037
            $class->setIdGeneratorType($parent->generatorType);
154 1037
            $this->addInheritedFields($class, $parent);
155 1037
            $this->addInheritedRelations($class, $parent);
156 1037
            $this->addInheritedIndexes($class, $parent);
157 1037
            $this->setInheritedShardKey($class, $parent);
158 1037
            $class->setIdentifier($parent->identifier);
159 1037
            $class->setVersioned($parent->isVersioned);
160 1037
            $class->setVersionField($parent->versionField);
161 1037
            $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
162 1037
            $class->setAlsoLoadMethods($parent->alsoLoadMethods);
163 1037
            $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
164 1037
            $class->setReadPreference($parent->readPreference, $parent->readPreferenceTags);
165 1037
            $class->setWriteConcern($parent->writeConcern);
166
167 1037
            if ($parent->isMappedSuperclass) {
168 970
                $class->setCustomRepositoryClass($parent->customRepositoryClassName);
169
            }
170
171 1037
            if ($parent->isFile) {
172 1
                $class->isFile = true;
173 1
                $class->setBucketName($parent->bucketName);
174
175 1
                if ($parent->chunkSizeBytes !== null) {
176 1
                    $class->setChunkSizeBytes($parent->chunkSizeBytes);
177
                }
178
            }
179
        }
180
181
        // Invoke driver
182
        try {
183 1606
            $this->driver->loadMetadataForClass($class->getName(), $class);
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
184 13
        } catch (ReflectionException $e) {
185
            throw MappingException::reflectionFailure($class->getName(), $e);
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
186
        }
187
188 1598
        $this->validateIdentifier($class);
189
190 1598
        if ($parent instanceof ClassMetadata && $rootEntityFound && $parent->generatorType === $class->generatorType) {
191 185
            if ($parent->generatorType) {
192 185
                $class->setIdGeneratorType($parent->generatorType);
193
            }
194 185
            if ($parent->generatorOptions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parent->generatorOptions 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...
195
                $class->setIdGeneratorOptions($parent->generatorOptions);
196
            }
197 185
            if ($parent->idGenerator) {
198 185
                $class->setIdGenerator($parent->idGenerator);
199
            }
200
        } else {
201 1598
            $this->completeIdGeneratorMapping($class);
202
        }
203
204 1598
        if ($parent instanceof ClassMetadata && $parent->isInheritanceTypeSingleCollection()) {
205 174
            $class->setDatabase($parent->getDatabase());
206 174
            $class->setCollection($parent->getCollection());
207
        }
208
209 1598
        $class->setParentClasses($nonSuperclassParents);
210
211 1598
        if (! $this->evm->hasListeners(Events::loadClassMetadata)) {
212 1594
            return;
213
        }
214
215 4
        $eventArgs = new LoadClassMetadataEventArgs($class, $this->dm);
216 4
        $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
217 4
    }
218
219
    /**
220
     * Validates the identifier mapping.
221
     *
222
     * @throws MappingException
223
     */
224 1598
    protected function validateIdentifier(ClassMetadata $class) : void
225
    {
226 1598
        if (! $class->identifier && $this->isEntity($class)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class->identifier of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
227
            throw MappingException::identifierRequired($class->name);
228
        }
229 1598
    }
230
231
    /**
232
     * {@inheritdoc}
233
     */
234 1606
    protected function newClassMetadataInstance($className) : ClassMetadata
235
    {
236 1606
        return new ClassMetadata($className);
237
    }
238
239 1598
    private function completeIdGeneratorMapping(ClassMetadata $class) : void
240
    {
241 1598
        $idGenOptions = $class->generatorOptions;
242 1598
        switch ($class->generatorType) {
243
            case ClassMetadata::GENERATOR_TYPE_AUTO:
244 1532
                $class->setIdGenerator(new AutoGenerator());
245 1532
                break;
246 View Code Duplication
            case ClassMetadata::GENERATOR_TYPE_INCREMENT:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
247 9
                $incrementGenerator = new IncrementGenerator();
248 9
                if (isset($idGenOptions['key'])) {
249
                    $incrementGenerator->setKey((string) $idGenOptions['key']);
250
                }
251 9
                if (isset($idGenOptions['collection'])) {
252
                    $incrementGenerator->setCollection((string) $idGenOptions['collection']);
253
                }
254 9
                if (isset($idGenOptions['startingId'])) {
255 1
                    $incrementGenerator->setStartingId((int) $idGenOptions['startingId']);
256
                }
257 9
                $class->setIdGenerator($incrementGenerator);
258 9
                break;
259
            case ClassMetadata::GENERATOR_TYPE_UUID:
260 4
                $uuidGenerator = new UuidGenerator();
261 4
                if (isset($idGenOptions['salt'])) {
262 2
                    $uuidGenerator->setSalt((string) $idGenOptions['salt']);
263
                }
264 4
                $class->setIdGenerator($uuidGenerator);
265 4
                break;
266 View Code Duplication
            case ClassMetadata::GENERATOR_TYPE_ALNUM:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
267 1
                $alnumGenerator = new AlnumGenerator();
268 1
                if (isset($idGenOptions['pad'])) {
269
                    $alnumGenerator->setPad((int) $idGenOptions['pad']);
270
                }
271 1
                if (isset($idGenOptions['chars'])) {
272 1
                    $alnumGenerator->setChars((string) $idGenOptions['chars']);
273
                } elseif (isset($idGenOptions['awkwardSafe'])) {
274
                    $alnumGenerator->setAwkwardSafeMode((bool) $idGenOptions['awkwardSafe']);
275
                }
276
277 1
                $class->setIdGenerator($alnumGenerator);
278 1
                break;
279
            case ClassMetadata::GENERATOR_TYPE_CUSTOM:
280
                if (empty($idGenOptions['class'])) {
281
                    throw MappingException::missingIdGeneratorClass($class->name);
282
                }
283
284
                $customGenerator = new $idGenOptions['class']();
285
                unset($idGenOptions['class']);
286
                if (! $customGenerator instanceof IdGenerator) {
287
                    throw MappingException::classIsNotAValidGenerator(get_class($customGenerator));
288
                }
289
290
                $methods = get_class_methods($customGenerator);
291
                foreach ($idGenOptions as $name => $value) {
292
                    $method = 'set' . ucfirst($name);
293
                    if (! in_array($method, $methods)) {
294
                        throw MappingException::missingGeneratorSetter(get_class($customGenerator), $name);
295
                    }
296
297
                    $customGenerator->$method($value);
298
                }
299
                $class->setIdGenerator($customGenerator);
300
                break;
301
            case ClassMetadata::GENERATOR_TYPE_NONE:
302 215
                break;
303
            default:
304
                throw new MappingException('Unknown generator type: ' . $class->generatorType);
305
        }
306 1598
    }
307
308
    /**
309
     * Adds inherited fields to the subclass mapping.
310
     */
311 1037
    private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass) : void
312
    {
313 1037
        foreach ($parentClass->fieldMappings as $fieldName => $mapping) {
314 205 View Code Duplication
            if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
315 197
                $mapping['inherited'] = $parentClass->name;
316
            }
317 205
            if (! isset($mapping['declared'])) {
318 205
                $mapping['declared'] = $parentClass->name;
319
            }
320 205
            $subClass->addInheritedFieldMapping($mapping);
321
        }
322 1037
        foreach ($parentClass->reflFields as $name => $field) {
323 205
            $subClass->reflFields[$name] = $field;
324
        }
325 1037
    }
326
327
    /**
328
     * Adds inherited association mappings to the subclass mapping.
329
     *
330
     * @throws MappingException
331
     */
332 1037
    private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass) : void
333
    {
334 1037
        foreach ($parentClass->associationMappings as $field => $mapping) {
335 153
            if ($parentClass->isMappedSuperclass) {
336 4
                $mapping['sourceDocument'] = $subClass->name;
337
            }
338
339 153 View Code Duplication
            if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
340 149
                $mapping['inherited'] = $parentClass->name;
341
            }
342 153
            if (! isset($mapping['declared'])) {
343 153
                $mapping['declared'] = $parentClass->name;
344
            }
345 153
            $subClass->addInheritedAssociationMapping($mapping);
346
        }
347 1037
    }
348
349
    /**
350
     * Adds inherited indexes to the subclass mapping.
351
     */
352 1037
    private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass) : void
353
    {
354 1037
        foreach ($parentClass->indexes as $index) {
355 126
            $subClass->addIndex($index['keys'], $index['options']);
356
        }
357 1037
    }
358
359
    /**
360
     * Adds inherited shard key to the subclass mapping.
361
     */
362 1037
    private function setInheritedShardKey(ClassMetadata $subClass, ClassMetadata $parentClass) : void
363
    {
364 1037
        if (! $parentClass->isSharded()) {
365 1032
            return;
366
        }
367
368 5
        $subClass->setShardKey(
369 5
            $parentClass->shardKey['keys'],
370 5
            $parentClass->shardKey['options']
371
        );
372 5
    }
373
}
374
375 1
interface_exists(ClassMetadataInterface::class);
376
interface_exists(ReflectionService::class);
377