Completed
Push — master ( 056d89...fc4c80 )
by Maciej
10s
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
namespace Doctrine\ODM\MongoDB\Mapping;
4
5
use Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory;
6
use Doctrine\Common\Persistence\Mapping\ClassMetadata as ClassMetadataInterface;
7
use Doctrine\Common\Persistence\Mapping\ReflectionService;
8
use Doctrine\ODM\MongoDB\Configuration;
9
use Doctrine\ODM\MongoDB\DocumentManager;
10
use Doctrine\ODM\MongoDB\Event\LoadClassMetadataEventArgs;
11
use Doctrine\ODM\MongoDB\Events;
12
use Doctrine\ODM\MongoDB\Id\AbstractIdGenerator;
13
use Doctrine\ODM\MongoDB\Id\AlnumGenerator;
14
use Doctrine\ODM\MongoDB\Id\AutoGenerator;
15
use Doctrine\ODM\MongoDB\Id\IncrementGenerator;
16
use Doctrine\ODM\MongoDB\Id\UuidGenerator;
17
18
/**
19
 * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
20
 * metadata mapping informations of a class which describes how a class should be mapped
21
 * to a document database.
22
 *
23
 * @since       1.0
24
 */
25
class ClassMetadataFactory extends AbstractClassMetadataFactory
26
{
27
    protected $cacheSalt = "\$MONGODBODMCLASSMETADATA";
28
29
    /** @var DocumentManager The DocumentManager instance */
30
    private $dm;
31
32
    /** @var Configuration The Configuration instance */
33
    private $config;
34
35
    /** @var \Doctrine\Common\Persistence\Mapping\Driver\MappingDriver The used metadata driver. */
36
    private $driver;
37
38
    /** @var \Doctrine\Common\EventManager The event manager instance */
39
    private $evm;
40
41
    /**
42
     * Sets the DocumentManager instance for this class.
43
     *
44
     * @param DocumentManager $dm The DocumentManager instance
45
     */
46 1619
    public function setDocumentManager(DocumentManager $dm)
47
    {
48 1619
        $this->dm = $dm;
49 1619
    }
50
51
    /**
52
     * Sets the Configuration instance
53
     *
54
     * @param Configuration $config
55
     */
56 1619
    public function setConfiguration(Configuration $config)
57
    {
58 1619
        $this->config = $config;
59 1619
    }
60
61
    /**
62
     * Lazy initialization of this stuff, especially the metadata driver,
63
     * since these are not needed at all when a metadata cache is active.
64
     */
65 1364
    protected function initialize()
66
    {
67 1364
        $this->driver = $this->config->getMetadataDriverImpl();
68 1364
        $this->evm = $this->dm->getEventManager();
69 1364
        $this->initialized = true;
70 1364
    }
71
72
    /**
73
     * {@inheritDoc}
74
     */
75
    protected function getFqcnFromAlias($namespaceAlias, $simpleClassName)
76
    {
77
        return $this->config->getDocumentNamespace($namespaceAlias) . '\\' . $simpleClassName;
78
    }
79
80
    /**
81
     * {@inheritDoc}
82
     */
83 887
    protected function getDriver()
84
    {
85 887
        return $this->driver;
86
    }
87
88
    /**
89
     * {@inheritDoc}
90
     */
91 1360
    protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService)
92
    {
93 1360
    }
94
95
    /**
96
     * {@inheritDoc}
97
     */
98 1364
    protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService)
99
    {
100 1364
    }
101
102
    /**
103
     * {@inheritDoc}
104
     */
105 1360
    protected function isEntity(ClassMetadataInterface $class)
106
    {
107 1360
        return ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument && ! $class->isQueryResultDocument;
108
    }
109
110
    /**
111
     * {@inheritDoc}
112
     */
113 1364
    protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents = array())
114
    {
115
        /** @var $class ClassMetadata */
116
        /** @var $parent ClassMetadata */
117 1364
        if ($parent) {
118 885
            $class->setInheritanceType($parent->inheritanceType);
119 885
            $class->setDiscriminatorField($parent->discriminatorField);
120 885
            $class->setDiscriminatorMap($parent->discriminatorMap);
121 885
            $class->setDefaultDiscriminatorValue($parent->defaultDiscriminatorValue);
122 885
            $class->setIdGeneratorType($parent->generatorType);
123 885
            $this->addInheritedFields($class, $parent);
124 885
            $this->addInheritedRelations($class, $parent);
125 885
            $this->addInheritedIndexes($class, $parent);
126 885
            $this->setInheritedShardKey($class, $parent);
127 885
            $class->setIdentifier($parent->identifier);
128 885
            $class->setVersioned($parent->isVersioned);
129 885
            $class->setVersionField($parent->versionField);
130 885
            $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
131 885
            $class->setAlsoLoadMethods($parent->alsoLoadMethods);
132 885
            $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
133 885
            $class->setReadPreference($parent->readPreference, $parent->readPreferenceTags);
134 885
            $class->setWriteConcern($parent->writeConcern);
135 885
            if ($parent->isMappedSuperclass) {
136 822
                $class->setCustomRepositoryClass($parent->customRepositoryClassName);
137
            }
138
        }
139
140
        // Invoke driver
141
        try {
142 1364
            $this->driver->loadMetadataForClass($class->getName(), $class);
143 6
        } catch (\ReflectionException $e) {
144
            throw MappingException::reflectionFailure($class->getName(), $e);
145
        }
146
147 1360
        $this->validateIdentifier($class);
148
149 1360
        if ($parent && $rootEntityFound && $parent->generatorType === $class->generatorType) {
150 98
            if ($parent->generatorType) {
151 98
                $class->setIdGeneratorType($parent->generatorType);
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Doctrine\Common\Persistence\Mapping\ClassMetadata as the method setIdGeneratorType() does only exist in the following implementations of said interface: Doctrine\ODM\MongoDB\Mapping\ClassMetadata.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

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