Completed
Push — master ( f3ed76...7db4b5 )
by Andreas
14s queued 10s
created

ODM/MongoDB/Mapping/ClassMetadataFactory.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
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\DocumentManager;
14
use Doctrine\ODM\MongoDB\Event\LoadClassMetadataEventArgs;
15
use Doctrine\ODM\MongoDB\Events;
16
use Doctrine\ODM\MongoDB\Id\AbstractIdGenerator;
17
use Doctrine\ODM\MongoDB\Id\AlnumGenerator;
18
use Doctrine\ODM\MongoDB\Id\AutoGenerator;
19
use Doctrine\ODM\MongoDB\Id\IncrementGenerator;
20
use Doctrine\ODM\MongoDB\Id\UuidGenerator;
21
use ReflectionException;
22
use function get_class;
23
use function get_class_methods;
24
use function in_array;
25
use function ucfirst;
26
27
/**
28
 * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
29
 * metadata mapping informations of a class which describes how a class should be mapped
30
 * to a document database.
31
 */
32
class ClassMetadataFactory extends AbstractClassMetadataFactory
33
{
34
    /** @var string */
35
    protected $cacheSalt = '$MONGODBODMCLASSMETADATA';
36
37
    /** @var DocumentManager The DocumentManager instance */
38
    private $dm;
39
40
    /** @var Configuration The Configuration instance */
41
    private $config;
42
43
    /** @var MappingDriver The used metadata driver. */
44
    private $driver;
45
46
    /** @var EventManager The event manager instance */
47
    private $evm;
48
49 1696
    public function setDocumentManager(DocumentManager $dm) : void
50
    {
51 1696
        $this->dm = $dm;
52 1696
    }
53
54 1696
    public function setConfiguration(Configuration $config) : void
55
    {
56 1696
        $this->config = $config;
57 1696
    }
58
59
    /**
60
     * Lazy initialization of this stuff, especially the metadata driver,
61
     * since these are not needed at all when a metadata cache is active.
62
     */
63 1434
    protected function initialize() : void
64
    {
65 1434
        $this->driver      = $this->config->getMetadataDriverImpl();
66 1434
        $this->evm         = $this->dm->getEventManager();
67 1434
        $this->initialized = true;
68 1434
    }
69
70
    /**
71
     * {@inheritDoc}
72
     */
73
    protected function getFqcnFromAlias($namespaceAlias, $simpleClassName) : string
74
    {
75
        return $this->config->getDocumentNamespace($namespaceAlias) . '\\' . $simpleClassName;
76
    }
77
78
    /**
79
     * {@inheritDoc}
80
     */
81 917
    protected function getDriver()
82
    {
83 917
        return $this->driver;
84
    }
85
86
    /**
87
     * {@inheritDoc}
88
     */
89 1430
    protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService) : void
90
    {
91 1430
    }
92
93
    /**
94
     * {@inheritDoc}
95
     */
96 1434
    protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService) : void
97
    {
98 1434
    }
99
100
    /**
101
     * {@inheritDoc}
102
     */
103 1430
    protected function isEntity(ClassMetadataInterface $class) : bool
104
    {
105 1430
        return ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument && ! $class->isQueryResultDocument;
106
    }
107
108
    /**
109
     * {@inheritDoc}
110
     */
111 1434
    protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents = []) : void
112
    {
113
        /** @var $class ClassMetadata */
114
        /** @var $parent ClassMetadata */
115 1434
        if ($parent) {
116 915
            $class->setInheritanceType($parent->inheritanceType);
117 915
            $class->setDiscriminatorField($parent->discriminatorField);
118 915
            $class->setDiscriminatorMap($parent->discriminatorMap);
119 915
            $class->setDefaultDiscriminatorValue($parent->defaultDiscriminatorValue);
120 915
            $class->setIdGeneratorType($parent->generatorType);
121 915
            $this->addInheritedFields($class, $parent);
0 ignored issues
show
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ODM\Mong...\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
122 915
            $this->addInheritedRelations($class, $parent);
123 915
            $this->addInheritedIndexes($class, $parent);
0 ignored issues
show
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ODM\Mong...\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
124 915
            $this->setInheritedShardKey($class, $parent);
125 915
            $class->setIdentifier($parent->identifier);
126 915
            $class->setVersioned($parent->isVersioned);
127 915
            $class->setVersionField($parent->versionField);
128 915
            $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
129 915
            $class->setAlsoLoadMethods($parent->alsoLoadMethods);
130 915
            $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
131 915
            $class->setReadPreference($parent->readPreference, $parent->readPreferenceTags);
132 915
            $class->setWriteConcern($parent->writeConcern);
133 915
            if ($parent->isMappedSuperclass) {
134 852
                $class->setCustomRepositoryClass($parent->customRepositoryClassName);
135
            }
136
        }
137
138
        // Invoke driver
139
        try {
140 1434
            $this->driver->loadMetadataForClass($class->getName(), $class);
141 6
        } catch (ReflectionException $e) {
142
            throw MappingException::reflectionFailure($class->getName(), $e);
143
        }
144
145 1430
        $this->validateIdentifier($class);
0 ignored issues
show
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ODM\Mong...\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
146
147 1430
        if ($parent && $rootEntityFound && $parent->generatorType === $class->generatorType) {
148 120
            if ($parent->generatorType) {
149 120
                $class->setIdGeneratorType($parent->generatorType);
150
            }
151 120
            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...
152
                $class->setIdGeneratorOptions($parent->generatorOptions);
153
            }
154 120
            if ($parent->idGenerator) {
155 120
                $class->setIdGenerator($parent->idGenerator);
156
            }
157
        } else {
158 1430
            $this->completeIdGeneratorMapping($class);
0 ignored issues
show
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ODM\Mong...\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
159
        }
160
161 1430
        if ($parent && $parent->isInheritanceTypeSingleCollection()) {
162 107
            $class->setDatabase($parent->getDatabase());
163 107
            $class->setCollection($parent->getCollection());
164
        }
165
166 1430
        $class->setParentClasses($nonSuperclassParents);
167
168 1430
        if (! $this->evm->hasListeners(Events::loadClassMetadata)) {
169 1428
            return;
170
        }
171
172 2
        $eventArgs = new LoadClassMetadataEventArgs($class, $this->dm);
173 2
        $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
174 2
    }
175
176
    /**
177
     * Validates the identifier mapping.
178
     *
179
     * @throws MappingException
180
     */
181 1430
    protected function validateIdentifier(ClassMetadata $class) : void
182
    {
183 1430
        if (! $class->identifier && ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument && ! $class->isQueryResultDocument) {
184
            throw MappingException::identifierRequired($class->name);
185
        }
186 1430
    }
187
188
    /**
189
     * {@inheritdoc}
190
     */
191 1434
    protected function newClassMetadataInstance($className) : ClassMetadata
192
    {
193 1434
        return new ClassMetadata($className);
194
    }
195
196 1430
    private function completeIdGeneratorMapping(ClassMetadata $class) : void
197
    {
198 1430
        $idGenOptions = $class->generatorOptions;
199 1430
        switch ($class->generatorType) {
200
            case ClassMetadata::GENERATOR_TYPE_AUTO:
201 1362
                $class->setIdGenerator(new AutoGenerator());
202 1362
                break;
203 View Code Duplication
            case ClassMetadata::GENERATOR_TYPE_INCREMENT:
204 9
                $incrementGenerator = new IncrementGenerator();
205 9
                if (isset($idGenOptions['key'])) {
206
                    $incrementGenerator->setKey((string) $idGenOptions['key']);
207
                }
208 9
                if (isset($idGenOptions['collection'])) {
209
                    $incrementGenerator->setCollection((string) $idGenOptions['collection']);
210
                }
211 9
                if (isset($idGenOptions['startingId'])) {
212 1
                    $incrementGenerator->setStartingId((int) $idGenOptions['startingId']);
213
                }
214 9
                $class->setIdGenerator($incrementGenerator);
215 9
                break;
216
            case ClassMetadata::GENERATOR_TYPE_UUID:
217 4
                $uuidGenerator = new UuidGenerator();
218 4
                isset($idGenOptions['salt']) && $uuidGenerator->setSalt((string) $idGenOptions['salt']);
219 4
                $class->setIdGenerator($uuidGenerator);
220 4
                break;
221 View Code Duplication
            case ClassMetadata::GENERATOR_TYPE_ALNUM:
222 1
                $alnumGenerator = new AlnumGenerator();
223 1
                if (isset($idGenOptions['pad'])) {
224
                    $alnumGenerator->setPad((int) $idGenOptions['pad']);
225
                }
226 1
                if (isset($idGenOptions['chars'])) {
227 1
                    $alnumGenerator->setChars((string) $idGenOptions['chars']);
228
                } elseif (isset($idGenOptions['awkwardSafe'])) {
229
                    $alnumGenerator->setAwkwardSafeMode((bool) $idGenOptions['awkwardSafe']);
230
                }
231
232 1
                $class->setIdGenerator($alnumGenerator);
233 1
                break;
234
            case ClassMetadata::GENERATOR_TYPE_CUSTOM:
235
                if (empty($idGenOptions['class'])) {
236
                    throw MappingException::missingIdGeneratorClass($class->name);
237
                }
238
239
                $customGenerator = new $idGenOptions['class']();
240
                unset($idGenOptions['class']);
241
                if (! $customGenerator instanceof AbstractIdGenerator) {
242
                    throw MappingException::classIsNotAValidGenerator(get_class($customGenerator));
243
                }
244
245
                $methods = get_class_methods($customGenerator);
246
                foreach ($idGenOptions as $name => $value) {
247
                    $method = 'set' . ucfirst($name);
248
                    if (! in_array($method, $methods)) {
249
                        throw MappingException::missingGeneratorSetter(get_class($customGenerator), $name);
250
                    }
251
252
                    $customGenerator->$method($value);
253
                }
254
                $class->setIdGenerator($customGenerator);
255
                break;
256
            case ClassMetadata::GENERATOR_TYPE_NONE:
257 150
                break;
258
            default:
259
                throw new MappingException('Unknown generator type: ' . $class->generatorType);
260
        }
261 1430
    }
262
263
    /**
264
     * Adds inherited fields to the subclass mapping.
265
     */
266 915
    private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass) : void
267
    {
268 915
        foreach ($parentClass->fieldMappings as $fieldName => $mapping) {
269 138 View Code Duplication
            if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
270 132
                $mapping['inherited'] = $parentClass->name;
271
            }
272 138
            if (! isset($mapping['declared'])) {
273 138
                $mapping['declared'] = $parentClass->name;
274
            }
275 138
            $subClass->addInheritedFieldMapping($mapping);
276
        }
277 915
        foreach ($parentClass->reflFields as $name => $field) {
278 138
            $subClass->reflFields[$name] = $field;
279
        }
280 915
    }
281
282
283
    /**
284
     * Adds inherited association mappings to the subclass mapping.
285
     *
286
     * @throws MappingException
287
     */
288 915
    private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass) : void
289
    {
290 915
        foreach ($parentClass->associationMappings as $field => $mapping) {
291 89
            if ($parentClass->isMappedSuperclass) {
292 4
                $mapping['sourceDocument'] = $subClass->name;
293
            }
294
295 89 View Code Duplication
            if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
296 85
                $mapping['inherited'] = $parentClass->name;
297
            }
298 89
            if (! isset($mapping['declared'])) {
299 89
                $mapping['declared'] = $parentClass->name;
300
            }
301 89
            $subClass->addInheritedAssociationMapping($mapping);
302
        }
303 915
    }
304
305
    /**
306
     * Adds inherited indexes to the subclass mapping.
307
     */
308 915
    private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass) : void
309
    {
310 915
        foreach ($parentClass->indexes as $index) {
311 65
            $subClass->addIndex($index['keys'], $index['options']);
312
        }
313 915
    }
314
315
    /**
316
     * Adds inherited shard key to the subclass mapping.
317
     */
318 915
    private function setInheritedShardKey(ClassMetadata $subClass, ClassMetadata $parentClass) : void
319
    {
320 915
        if (! $parentClass->isSharded()) {
321 910
            return;
322
        }
323
324 5
        $subClass->setShardKey(
325 5
            $parentClass->shardKey['keys'],
326 5
            $parentClass->shardKey['options']
327
        );
328 5
    }
329
}
330