Completed
Push — master ( a8fe50...bce26f )
by Maciej
13s
created

ODM/MongoDB/Mapping/ClassMetadataFactory.php (1 issue)

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 function get_class;
22
use function get_class_methods;
23
use function in_array;
24
use function ucfirst;
25
26
/**
27
 * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
28
 * metadata mapping informations of a class which describes how a class should be mapped
29
 * to a document database.
30
 *
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 1668
    public function setDocumentManager(DocumentManager $dm): void
50
    {
51 1668
        $this->dm = $dm;
52 1668
    }
53
54 1668
    public function setConfiguration(Configuration $config): void
55
    {
56 1668
        $this->config = $config;
57 1668
    }
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 1406
    protected function initialize(): void
64
    {
65 1406
        $this->driver = $this->config->getMetadataDriverImpl();
66 1406
        $this->evm = $this->dm->getEventManager();
67 1406
        $this->initialized = true;
68 1406
    }
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 913
    protected function getDriver()
82
    {
83 913
        return $this->driver;
84
    }
85
86
    /**
87
     * {@inheritDoc}
88
     */
89 1402
    protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService): void
90
    {
91 1402
    }
92
93
    /**
94
     * {@inheritDoc}
95
     */
96 1406
    protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService): void
97
    {
98 1406
    }
99
100
    /**
101
     * {@inheritDoc}
102
     */
103 1402
    protected function isEntity(ClassMetadataInterface $class): bool
104
    {
105 1402
        return ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument && ! $class->isQueryResultDocument;
106
    }
107
108
    /**
109
     * {@inheritDoc}
110
     */
111 1406
    protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents = []): void
112
    {
113
        /** @var $class ClassMetadata */
114
        /** @var $parent ClassMetadata */
115 1406
        if ($parent) {
116 911
            $class->setInheritanceType($parent->inheritanceType);
117 911
            $class->setDiscriminatorField($parent->discriminatorField);
118 911
            $class->setDiscriminatorMap($parent->discriminatorMap);
119 911
            $class->setDefaultDiscriminatorValue($parent->defaultDiscriminatorValue);
120 911
            $class->setIdGeneratorType($parent->generatorType);
121 911
            $this->addInheritedFields($class, $parent);
122 911
            $this->addInheritedRelations($class, $parent);
123 911
            $this->addInheritedIndexes($class, $parent);
124 911
            $this->setInheritedShardKey($class, $parent);
125 911
            $class->setIdentifier($parent->identifier);
126 911
            $class->setVersioned($parent->isVersioned);
127 911
            $class->setVersionField($parent->versionField);
128 911
            $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
129 911
            $class->setAlsoLoadMethods($parent->alsoLoadMethods);
130 911
            $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
131 911
            $class->setReadPreference($parent->readPreference, $parent->readPreferenceTags);
132 911
            $class->setWriteConcern($parent->writeConcern);
133 911
            if ($parent->isMappedSuperclass) {
134 848
                $class->setCustomRepositoryClass($parent->customRepositoryClassName);
135
            }
136
        }
137
138
        // Invoke driver
139
        try {
140 1406
            $this->driver->loadMetadataForClass($class->getName(), $class);
141 6
        } catch (\ReflectionException $e) {
142
            throw MappingException::reflectionFailure($class->getName(), $e);
143
        }
144
145 1402
        $this->validateIdentifier($class);
146
147 1402
        if ($parent && $rootEntityFound && $parent->generatorType === $class->generatorType) {
148 118
            if ($parent->generatorType) {
149 118
                $class->setIdGeneratorType($parent->generatorType);
150
            }
151 118
            if ($parent->generatorOptions) {
152
                $class->setIdGeneratorOptions($parent->generatorOptions);
153
            }
154 118
            if ($parent->idGenerator) {
155 118
                $class->setIdGenerator($parent->idGenerator);
156
            }
157
        } else {
158 1402
            $this->completeIdGeneratorMapping($class);
159
        }
160
161 1402
        if ($parent && $parent->isInheritanceTypeSingleCollection()) {
162 107
            $class->setDatabase($parent->getDatabase());
163 107
            $class->setCollection($parent->getCollection());
164
        }
165
166 1402
        $class->setParentClasses($nonSuperclassParents);
167
168 1402
        if (! $this->evm->hasListeners(Events::loadClassMetadata)) {
169 1400
            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 1402
    protected function validateIdentifier(ClassMetadata $class): void
182
    {
183 1402
        if (! $class->identifier && ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument && ! $class->isQueryResultDocument) {
184
            throw MappingException::identifierRequired($class->name);
185
        }
186 1402
    }
187
188
    /**
189
     * {@inheritdoc}
190
     */
191 1406
    protected function newClassMetadataInstance($className): ClassMetadata
192
    {
193 1406
        return new ClassMetadata($className);
194
    }
195
196 1402
    private function completeIdGeneratorMapping(ClassMetadata $class): void
197
    {
198 1402
        $idGenOptions = $class->generatorOptions;
199 1402
        switch ($class->generatorType) {
200
            case ClassMetadata::GENERATOR_TYPE_AUTO:
201 1334
                $class->setIdGenerator(new AutoGenerator());
202 1334
                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 1402
    }
262
263
    /**
264
     * Adds inherited fields to the subclass mapping.
265
     */
266 911
    private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass): void
267
    {
268 911
        foreach ($parentClass->fieldMappings as $fieldName => $mapping) {
269 136 View Code Duplication
            if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
270 130
                $mapping['inherited'] = $parentClass->name;
271
            }
272 136
            if (! isset($mapping['declared'])) {
273 136
                $mapping['declared'] = $parentClass->name;
274
            }
275 136
            $subClass->addInheritedFieldMapping($mapping);
276
        }
277 911
        foreach ($parentClass->reflFields as $name => $field) {
278 136
            $subClass->reflFields[$name] = $field;
279
        }
280 911
    }
281
282
283
    /**
284
     * Adds inherited association mappings to the subclass mapping.
285
     *
286
     * @throws MappingException
287
     */
288 911
    private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass): void
289
    {
290 911
        foreach ($parentClass->associationMappings as $field => $mapping) {
291 87
            if ($parentClass->isMappedSuperclass) {
292 4
                $mapping['sourceDocument'] = $subClass->name;
293
            }
294
295 87 View Code Duplication
            if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
296 83
                $mapping['inherited'] = $parentClass->name;
297
            }
298 87
            if (! isset($mapping['declared'])) {
299 87
                $mapping['declared'] = $parentClass->name;
300
            }
301 87
            $subClass->addInheritedAssociationMapping($mapping);
302
        }
303 911
    }
304
305
    /**
306
     * Adds inherited indexes to the subclass mapping.
307
     */
308 911
    private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass): void
309
    {
310 911
        foreach ($parentClass->indexes as $index) {
311 65
            $subClass->addIndex($index['keys'], $index['options']);
312
        }
313 911
    }
314
315
    /**
316
     * Adds inherited shard key to the subclass mapping.
317
     */
318 911
    private function setInheritedShardKey(ClassMetadata $subClass, ClassMetadata $parentClass): void
319
    {
320 911
        if (! $parentClass->isSharded()) {
321 906
            return;
322
        }
323
324 5
        $subClass->setShardKey(
325 5
            $parentClass->shardKey['keys'],
326 5
            $parentClass->shardKey['options']
0 ignored issues
show
$parentClass->shardKey['options'] is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
327
        );
328 5
    }
329
}
330