Completed
Push — master ( 7b9f4b...1cd743 )
by Andreas
13s queued 10s
created

lib/Doctrine/ODM/MongoDB/DocumentManager.php (3 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;
6
7
use Doctrine\Common\EventManager;
8
use Doctrine\Common\Persistence\ObjectManager;
9
use Doctrine\Common\Persistence\ObjectRepository;
10
use Doctrine\ODM\MongoDB\Hydrator\HydratorFactory;
11
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
12
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory;
13
use Doctrine\ODM\MongoDB\Mapping\MappingException;
14
use Doctrine\ODM\MongoDB\Proxy\ClassNameResolver;
15
use Doctrine\ODM\MongoDB\Proxy\Factory\ProxyFactory;
16
use Doctrine\ODM\MongoDB\Proxy\Factory\StaticProxyFactory;
17
use Doctrine\ODM\MongoDB\Query\FilterCollection;
18
use Doctrine\ODM\MongoDB\Repository\DocumentRepository;
19
use Doctrine\ODM\MongoDB\Repository\RepositoryFactory;
20
use InvalidArgumentException;
21
use MongoDB\Client;
22
use MongoDB\Collection;
23
use MongoDB\Database;
24
use MongoDB\Driver\ReadPreference;
25
use MongoDB\GridFS\Bucket;
26
use RuntimeException;
27
use function array_search;
28
use function assert;
29
use function get_class;
30
use function gettype;
31
use function is_object;
32
use function ltrim;
33
use function sprintf;
34
35
/**
36
 * The DocumentManager class is the central access point for managing the
37
 * persistence of documents.
38
 *
39
 *     <?php
40
 *
41
 *     $config = new Configuration();
42
 *     $dm = DocumentManager::create(new Connection(), $config);
43
 */
44
class DocumentManager implements ObjectManager
45
{
46
    public const CLIENT_TYPEMAP = ['root' => 'array', 'document' => 'array'];
47
48
    /**
49
     * The Doctrine MongoDB connection instance.
50
     *
51
     * @var Client
52
     */
53
    private $client;
54
55
    /**
56
     * The used Configuration.
57
     *
58
     * @var Configuration
59
     */
60
    private $config;
61
62
    /**
63
     * The metadata factory, used to retrieve the ODM metadata of document classes.
64
     *
65
     * @var ClassMetadataFactory
66
     */
67
    private $metadataFactory;
68
69
    /**
70
     * The UnitOfWork used to coordinate object-level transactions.
71
     *
72
     * @var UnitOfWork
73
     */
74
    private $unitOfWork;
75
76
    /**
77
     * The event manager that is the central point of the event system.
78
     *
79
     * @var EventManager
80
     */
81
    private $eventManager;
82
83
    /**
84
     * The Hydrator factory instance.
85
     *
86
     * @var HydratorFactory
87
     */
88
    private $hydratorFactory;
89
90
    /**
91
     * The Proxy factory instance.
92
     *
93
     * @var ProxyFactory
94
     */
95
    private $proxyFactory;
96
97
    /**
98
     * The repository factory used to create dynamic repositories.
99
     *
100
     * @var RepositoryFactory
101
     */
102
    private $repositoryFactory;
103
104
    /**
105
     * SchemaManager instance
106
     *
107
     * @var SchemaManager
108
     */
109
    private $schemaManager;
110
111
    /**
112
     * Array of cached document database instances that are lazily loaded.
113
     *
114
     * @var Database[]
115
     */
116
    private $documentDatabases = [];
117
118
    /**
119
     * Array of cached document collection instances that are lazily loaded.
120
     *
121
     * @var Collection[]
122
     */
123
    private $documentCollections = [];
124
125
    /**
126
     * Array of cached document bucket instances that are lazily loaded.
127
     *
128
     * @var Bucket[]
129
     */
130
    private $documentBuckets = [];
131
132
    /**
133
     * Whether the DocumentManager is closed or not.
134
     *
135
     * @var bool
136
     */
137
    private $closed = false;
138
139
    /**
140
     * Collection of query filters.
141
     *
142
     * @var FilterCollection
143
     */
144
    private $filterCollection;
145
146
    /** @var ClassNameResolver */
147
    private $classNameResolver;
148
149
    /**
150
     * Creates a new Document that operates on the given Mongo connection
151
     * and uses the given Configuration.
152
     */
153 1651
    protected function __construct(?Client $client = null, ?Configuration $config = null, ?EventManager $eventManager = null)
154
    {
155 1651
        $this->config       = $config ?: new Configuration();
156 1651
        $this->eventManager = $eventManager ?: new EventManager();
157 1651
        $this->client       = $client ?: new Client('mongodb://127.0.0.1', [], ['typeMap' => self::CLIENT_TYPEMAP]);
158
159 1651
        $this->checkTypeMap();
160
161 1651
        $metadataFactoryClassName = $this->config->getClassMetadataFactoryName();
162 1651
        $this->metadataFactory    = new $metadataFactoryClassName();
163 1651
        $this->metadataFactory->setDocumentManager($this);
164 1651
        $this->metadataFactory->setConfiguration($this->config);
165
166 1651
        $cacheDriver = $this->config->getMetadataCacheImpl();
167 1651
        if ($cacheDriver) {
168
            $this->metadataFactory->setCacheDriver($cacheDriver);
169
        }
170
171 1651
        $hydratorDir           = $this->config->getHydratorDir();
172 1651
        $hydratorNs            = $this->config->getHydratorNamespace();
173 1651
        $this->hydratorFactory = new HydratorFactory(
174 1651
            $this,
175 1651
            $this->eventManager,
176 1651
            $hydratorDir,
177 1651
            $hydratorNs,
178 1651
            $this->config->getAutoGenerateHydratorClasses()
179
        );
180
181 1651
        $this->unitOfWork = new UnitOfWork($this, $this->eventManager, $this->hydratorFactory);
182 1651
        $this->hydratorFactory->setUnitOfWork($this->unitOfWork);
183 1651
        $this->schemaManager     = new SchemaManager($this, $this->metadataFactory);
184 1651
        $this->proxyFactory      = new StaticProxyFactory($this);
185 1651
        $this->repositoryFactory = $this->config->getRepositoryFactory();
186 1651
        $this->classNameResolver = new ClassNameResolver($this->config);
187 1651
    }
188
189
    /**
190
     * Gets the proxy factory used by the DocumentManager to create document proxies.
191
     */
192 1
    public function getProxyFactory() : ProxyFactory
193
    {
194 1
        return $this->proxyFactory;
195
    }
196
197
    /**
198
     * Creates a new Document that operates on the given Mongo connection
199
     * and uses the given Configuration.
200
     */
201 1651
    public static function create(?Client $client = null, ?Configuration $config = null, ?EventManager $eventManager = null) : DocumentManager
202
    {
203 1651
        return new static($client, $config, $eventManager);
204
    }
205
206
    /**
207
     * Gets the EventManager used by the DocumentManager.
208
     */
209 1757
    public function getEventManager() : EventManager
210
    {
211 1757
        return $this->eventManager;
212
    }
213
214
    /**
215
     * Gets the MongoDB client instance that this DocumentManager wraps.
216
     */
217 1651
    public function getClient() : Client
218
    {
219 1651
        return $this->client;
220
    }
221
222
    /**
223
     * Gets the metadata factory used to gather the metadata of classes.
224
     *
225
     * @return ClassMetadataFactory
226
     */
227 5
    public function getMetadataFactory()
228
    {
229 5
        return $this->metadataFactory;
230
    }
231
232
    /**
233
     * Helper method to initialize a lazy loading proxy or persistent collection.
234
     *
235
     * This method is a no-op for other objects.
236
     *
237
     * @param object $obj
238
     */
239
    public function initializeObject($obj)
240
    {
241
        $this->unitOfWork->initializeObject($obj);
242
    }
243
244
    /**
245
     * Gets the UnitOfWork used by the DocumentManager to coordinate operations.
246
     */
247 1675
    public function getUnitOfWork() : UnitOfWork
248
    {
249 1675
        return $this->unitOfWork;
250
    }
251
252
    /**
253
     * Gets the Hydrator factory used by the DocumentManager to generate and get hydrators
254
     * for each type of document.
255
     */
256 67
    public function getHydratorFactory() : HydratorFactory
257
    {
258 67
        return $this->hydratorFactory;
259
    }
260
261
    /**
262
     * Returns SchemaManager, used to create/drop indexes/collections/databases.
263
     */
264 31
    public function getSchemaManager() : SchemaManager
265
    {
266 31
        return $this->schemaManager;
267
    }
268
269
    /** Returns the class name resolver which is used to resolve real class names for proxy objects. */
270 1485
    public function getClassNameResolver() : ClassNameResolver
271
    {
272 1485
        return $this->classNameResolver;
273
    }
274
275
    /**
276
     * Returns the metadata for a class.
277
     *
278
     * @internal Performance-sensitive method.
279
     *
280
     * @param string $className The class name.
281
     */
282 1425
    public function getClassMetadata($className) : ClassMetadata
283
    {
284 1425
        return $this->metadataFactory->getMetadataFor($className);
285
    }
286
287
    /**
288
     * Returns the MongoDB instance for a class.
289
     */
290 1320
    public function getDocumentDatabase(string $className) : Database
291
    {
292 1320
        $className = $this->classNameResolver->getRealClass($className);
293
294 1320
        if (isset($this->documentDatabases[$className])) {
295 61
            return $this->documentDatabases[$className];
296
        }
297
298 1300
        $metadata = $this->metadataFactory->getMetadataFor($className);
299 1300
        assert($metadata instanceof ClassMetadata);
300
301 1300
        $db                                  = $metadata->getDatabase();
302 1300
        $db                                  = $db ?: $this->config->getDefaultDB();
303 1300
        $db                                  = $db ?: 'doctrine';
304 1300
        $this->documentDatabases[$className] = $this->client->selectDatabase($db);
305
306 1300
        return $this->documentDatabases[$className];
307
    }
308
309
    /**
310
     * Gets the array of instantiated document database instances.
311
     *
312
     * @return Database[]
313
     */
314
    public function getDocumentDatabases() : array
315
    {
316
        return $this->documentDatabases;
317
    }
318
319
    /**
320
     * Returns the collection instance for a class.
321
     *
322
     * @throws MongoDBException When the $className param is not mapped to a collection.
323
     */
324 1340
    public function getDocumentCollection(string $className) : Collection
325
    {
326 1340
        $className = $this->classNameResolver->getRealClass($className);
327
328
        /** @var ClassMetadata $metadata */
329 1340
        $metadata = $this->metadataFactory->getMetadataFor($className);
330 1340
        assert($metadata instanceof ClassMetadata);
331 1340
        if ($metadata->isFile) {
332 31
            return $this->getDocumentBucket($className)->getFilesCollection();
333
        }
334
335 1323
        $collectionName = $metadata->getCollection();
336
337 1323
        if (! $collectionName) {
338
            throw MongoDBException::documentNotMappedToCollection($className);
339
        }
340
341 1323
        if (! isset($this->documentCollections[$className])) {
342 1287
            $db = $this->getDocumentDatabase($className);
343
344 1287
            $options = [];
345 1287
            if ($metadata->readPreference !== null) {
346 3
                $options['readPreference'] = new ReadPreference($metadata->readPreference, $metadata->readPreferenceTags);
347
            }
348
349 1287
            $this->documentCollections[$className] = $db->selectCollection($collectionName, $options);
350
        }
351
352 1323
        return $this->documentCollections[$className];
353
    }
354
355
    /**
356
     * Returns the bucket instance for a class.
357
     *
358
     * @throws MongoDBException When the $className param is not mapped to a collection.
359
     */
360 31
    public function getDocumentBucket(string $className) : Bucket
361
    {
362 31
        $className = $this->classNameResolver->getRealClass($className);
363
364
        /** @var ClassMetadata $metadata */
365 31
        $metadata = $this->metadataFactory->getMetadataFor($className);
366 31
        if (! $metadata->isFile) {
367
            throw MongoDBException::documentBucketOnlyAvailableForGridFSFiles($className);
368
        }
369
370 31
        $bucketName = $metadata->getBucketName();
371
372 31
        if (! $bucketName) {
373
            throw MongoDBException::documentNotMappedToCollection($className);
374
        }
375
376 31
        if (! isset($this->documentBuckets[$className])) {
377 11
            $db = $this->getDocumentDatabase($className);
378
379 11
            $options = ['bucketName' => $bucketName];
380 11
            if ($metadata->readPreference !== null) {
381
                $options['readPreference'] = new ReadPreference($metadata->readPreference, $metadata->readPreferenceTags);
382
            }
383
384 11
            $this->documentBuckets[$className] = $db->selectGridFSBucket($options);
385
        }
386
387 31
        return $this->documentBuckets[$className];
388
    }
389
390
    /**
391
     * Gets the array of instantiated document collection instances.
392
     *
393
     * @return Collection[]
394
     */
395
    public function getDocumentCollections() : array
396
    {
397
        return $this->documentCollections;
398
    }
399
400
    /**
401
     * Create a new Query instance for a class.
402
     *
403
     * @param string[]|string|null $documentName (optional) an array of document names, the document name, or none
404
     */
405 179
    public function createQueryBuilder($documentName = null) : Query\Builder
406
    {
407 179
        return new Query\Builder($this, $documentName);
408
    }
409
410
    /**
411
     * Creates a new aggregation builder instance for a class.
412
     */
413 41
    public function createAggregationBuilder(string $documentName) : Aggregation\Builder
414
    {
415 41
        return new Aggregation\Builder($this, $documentName);
416
    }
417
418
    /**
419
     * Tells the DocumentManager to make an instance managed and persistent.
420
     *
421
     * The document will be entered into the database at or before transaction
422
     * commit or as a result of the flush operation.
423
     *
424
     * NOTE: The persist operation always considers documents that are not yet known to
425
     * this DocumentManager as NEW. Do not pass detached documents to the persist operation.
426
     *
427
     * @param object $document The instance to make managed and persistent.
428
     *
429
     * @throws InvalidArgumentException When the given $document param is not an object.
430
     */
431 623
    public function persist($document)
432
    {
433 623
        if (! is_object($document)) {
434 1
            throw new InvalidArgumentException(gettype($document));
435
        }
436 622
        $this->errorIfClosed();
437 621
        $this->unitOfWork->persist($document);
438 617
    }
439
440
    /**
441
     * Removes a document instance.
442
     *
443
     * A removed document will be removed from the database at or before transaction commit
444
     * or as a result of the flush operation.
445
     *
446
     * @param object $document The document instance to remove.
447
     *
448
     * @throws InvalidArgumentException When the $document param is not an object.
449
     */
450 27
    public function remove($document)
451
    {
452 27
        if (! is_object($document)) {
453 1
            throw new InvalidArgumentException(gettype($document));
454
        }
455 26
        $this->errorIfClosed();
456 25
        $this->unitOfWork->remove($document);
457 25
    }
458
459
    /**
460
     * Refreshes the persistent state of a document from the database,
461
     * overriding any local changes that have not yet been persisted.
462
     *
463
     * @param object $document The document to refresh.
464
     *
465
     * @throws InvalidArgumentException When the given $document param is not an object.
466
     */
467 26
    public function refresh($document)
468
    {
469 26
        if (! is_object($document)) {
470 1
            throw new InvalidArgumentException(gettype($document));
471
        }
472 25
        $this->errorIfClosed();
473 24
        $this->unitOfWork->refresh($document);
474 23
    }
475
476
    /**
477
     * Detaches a document from the DocumentManager, causing a managed document to
478
     * become detached.  Unflushed changes made to the document if any
479
     * (including removal of the document), will not be synchronized to the database.
480
     * Documents which previously referenced the detached document will continue to
481
     * reference it.
482
     *
483
     * @param object $document The document to detach.
484
     *
485
     * @throws InvalidArgumentException When the $document param is not an object.
486
     */
487 11
    public function detach($document)
488
    {
489 11
        if (! is_object($document)) {
490 1
            throw new InvalidArgumentException(gettype($document));
491
        }
492 10
        $this->unitOfWork->detach($document);
493 10
    }
494
495
    /**
496
     * Merges the state of a detached document into the persistence context
497
     * of this DocumentManager and returns the managed copy of the document.
498
     * The document passed to merge will not become associated/managed with this DocumentManager.
499
     *
500
     * @param object $document The detached document to merge into the persistence context.
501
     *
502
     * @return object The managed copy of the document.
503
     *
504
     * @throws LockException
505
     * @throws InvalidArgumentException If the $document param is not an object.
506
     */
507 13
    public function merge($document)
508
    {
509 13
        if (! is_object($document)) {
510 1
            throw new InvalidArgumentException(gettype($document));
511
        }
512 12
        $this->errorIfClosed();
513 11
        return $this->unitOfWork->merge($document);
514
    }
515
516
    /**
517
     * Acquire a lock on the given document.
518
     *
519
     * @throws InvalidArgumentException
520
     * @throws LockException
521
     */
522 8
    public function lock(object $document, int $lockMode, ?int $lockVersion = null) : void
523
    {
524 8
        $this->unitOfWork->lock($document, $lockMode, $lockVersion);
525 5
    }
526
527
    /**
528
     * Releases a lock on the given document.
529
     */
530 1
    public function unlock(object $document) : void
531
    {
532 1
        $this->unitOfWork->unlock($document);
533 1
    }
534
535
    /**
536
     * Gets the repository for a document class.
537
     *
538
     * @param string $documentName The name of the Document.
539
     *
540
     * @return ObjectRepository  The repository.
541
     */
542 351
    public function getRepository($documentName)
543
    {
544 351
        return $this->repositoryFactory->getRepository($this, $documentName);
545
    }
546
547
    /**
548
     * Flushes all changes to objects that have been queued up to now to the database.
549
     * This effectively synchronizes the in-memory state of managed objects with the
550
     * database.
551
     *
552
     * @param array $options Array of options to be used with batchInsert(), update() and remove()
553
     *
554
     * @throws MongoDBException
555
     */
556 595
    public function flush(array $options = [])
557
    {
558 595
        $this->errorIfClosed();
559 594
        $this->unitOfWork->commit($options);
560 579
    }
561
562
    /**
563
     * Gets a reference to the document identified by the given type and identifier
564
     * without actually loading it.
565
     *
566
     * If partial objects are allowed, this method will return a partial object that only
567
     * has its identifier populated. Otherwise a proxy is returned that automatically
568
     * loads itself on first access.
569
     *
570
     * @param mixed $identifier
571
     */
572 127
    public function getReference(string $documentName, $identifier) : object
573
    {
574
        /** @var ClassMetadata $class */
575 127
        $class    = $this->metadataFactory->getMetadataFor(ltrim($documentName, '\\'));
576 127
        $document = $this->unitOfWork->tryGetById($identifier, $class);
577
578
        // Check identity map first, if its already in there just return it.
579 127
        if ($document) {
580 56
            return $document;
581
        }
582
583 98
        $document = $this->proxyFactory->getProxy($class, $identifier);
584 98
        $this->unitOfWork->registerManaged($document, $identifier, []);
585
586 98
        return $document;
587
    }
588
589
    /**
590
     * Gets a partial reference to the document identified by the given type and identifier
591
     * without actually loading it, if the document is not yet loaded.
592
     *
593
     * The returned reference may be a partial object if the document is not yet loaded/managed.
594
     * If it is a partial object it will not initialize the rest of the document state on access.
595
     * Thus you can only ever safely access the identifier of a document obtained through
596
     * this method.
597
     *
598
     * The use-cases for partial references involve maintaining bidirectional associations
599
     * without loading one side of the association or to update a document without loading it.
600
     * Note, however, that in the latter case the original (persistent) document data will
601
     * never be visible to the application (especially not event listeners) as it will
602
     * never be loaded in the first place.
603
     *
604
     * @param mixed $identifier The document identifier.
605
     */
606 1
    public function getPartialReference(string $documentName, $identifier) : object
607
    {
608 1
        $class = $this->metadataFactory->getMetadataFor(ltrim($documentName, '\\'));
609 1
        assert($class instanceof ClassMetadata);
610 1
        $document = $this->unitOfWork->tryGetById($identifier, $class);
611
612
        // Check identity map first, if its already in there just return it.
613 1
        if ($document) {
614
            return $document;
615
        }
616 1
        $document = $class->newInstance();
617 1
        $class->setIdentifierValue($document, $identifier);
618 1
        $this->unitOfWork->registerManaged($document, $identifier, []);
619
620 1
        return $document;
621
    }
622
623
    /**
624
     * Finds a Document by its identifier.
625
     *
626
     * This is just a convenient shortcut for getRepository($documentName)->find($id).
627
     *
628
     * @param string $documentName
629
     * @param mixed  $identifier
630
     * @param int    $lockMode
631
     * @param int    $lockVersion
632
     */
633 174
    public function find($documentName, $identifier, $lockMode = LockMode::NONE, $lockVersion = null) : ?object
634
    {
635 174
        $repository = $this->getRepository($documentName);
636 174
        if ($repository instanceof DocumentRepository) {
637 174
            return $repository->find($identifier, $lockMode, $lockVersion);
638
        }
639
640
        return $repository->find($identifier);
641
    }
642
643
    /**
644
     * Clears the DocumentManager.
645
     *
646
     * All documents that are currently managed by this DocumentManager become
647
     * detached.
648
     *
649
     * @param string|null $documentName if given, only documents of this type will get detached
650
     */
651 377
    public function clear($documentName = null)
652
    {
653 377
        $this->unitOfWork->clear($documentName);
654 377
    }
655
656
    /**
657
     * Closes the DocumentManager. All documents that are currently managed
658
     * by this DocumentManager become detached. The DocumentManager may no longer
659
     * be used after it is closed.
660
     */
661 6
    public function close()
662
    {
663 6
        $this->clear();
664 6
        $this->closed = true;
665 6
    }
666
667
    /**
668
     * Determines whether a document instance is managed in this DocumentManager.
669
     *
670
     * @param object $document
671
     *
672
     * @return bool TRUE if this DocumentManager currently manages the given document, FALSE otherwise.
673
     *
674
     * @throws InvalidArgumentException When the $document param is not an object.
675
     */
676 3
    public function contains($document)
677
    {
678 3
        if (! is_object($document)) {
679
            throw new InvalidArgumentException(gettype($document));
680
        }
681 3
        return $this->unitOfWork->isScheduledForInsert($document) ||
682 3
            $this->unitOfWork->isInIdentityMap($document) &&
683 3
            ! $this->unitOfWork->isScheduledForDelete($document);
684
    }
685
686
    /**
687
     * Gets the Configuration used by the DocumentManager.
688
     */
689 1757
    public function getConfiguration() : Configuration
690
    {
691 1757
        return $this->config;
692
    }
693
694
    /**
695
     * Returns a reference to the supplied document.
696
     *
697
     * @return mixed The reference for the document in question, according to the desired mapping
698
     *
699
     * @throws MappingException
700
     * @throws RuntimeException
701
     */
702 226
    public function createReference(object $document, array $referenceMapping)
703
    {
704 226
        $class = $this->getClassMetadata(get_class($document));
705 226
        $id    = $this->unitOfWork->getDocumentIdentifier($document);
706
707 226
        if ($id === null) {
708 1
            throw new RuntimeException(
709 1
                sprintf('Cannot create a DBRef for class %s without an identifier. Have you forgotten to persist/merge the document first?', $class->name)
0 ignored issues
show
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
710
            );
711
        }
712
713 225
        $storeAs = $referenceMapping['storeAs'] ?? null;
714 225
        switch ($storeAs) {
715
            case ClassMetadata::REFERENCE_STORE_AS_ID:
716 46
                if ($class->inheritanceType === ClassMetadata::INHERITANCE_TYPE_SINGLE_COLLECTION) {
0 ignored issues
show
Accessing inheritanceType on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
717 1
                    throw MappingException::simpleReferenceMustNotTargetDiscriminatedDocument($referenceMapping['targetDocument']);
718
                }
719
720 45
                return $class->getDatabaseIdentifierValue($id);
721
                break;
722
723
            case ClassMetadata::REFERENCE_STORE_AS_REF:
724 20
                $reference = ['id' => $class->getDatabaseIdentifierValue($id)];
725 20
                break;
726
727
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF:
728
                $reference = [
729 181
                    '$ref' => $class->getCollection(),
730 181
                    '$id'  => $class->getDatabaseIdentifierValue($id),
731
                ];
732 181
                break;
733
734
            case ClassMetadata::REFERENCE_STORE_AS_DB_REF_WITH_DB:
735
                $reference = [
736 17
                    '$ref' => $class->getCollection(),
737 17
                    '$id'  => $class->getDatabaseIdentifierValue($id),
738 17
                    '$db'  => $this->getDocumentDatabase($class->name)->getDatabaseName(),
0 ignored issues
show
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
739
                ];
740 17
                break;
741
742
            default:
743
                throw new InvalidArgumentException(sprintf('Reference type %s is invalid.', $storeAs));
744
        }
745
746 203
        return $reference + $this->getDiscriminatorData($referenceMapping, $class);
747
    }
748
749
    /**
750
     * Build discriminator portion of reference for specified reference mapping and class metadata.
751
     *
752
     * @param array         $referenceMapping Mappings of reference for which discriminator data is created.
753
     * @param ClassMetadata $class            Metadata of reference document class.
754
     *
755
     * @return array with next structure [{discriminator field} => {discriminator value}]
756
     *
757
     * @throws MappingException When discriminator map is present and reference class in not registered in it.
758
     */
759 203
    private function getDiscriminatorData(array $referenceMapping, ClassMetadata $class) : array
760
    {
761 203
        $discriminatorField = null;
762 203
        $discriminatorValue = null;
763 203
        $discriminatorData  = [];
764 203
        if (isset($referenceMapping['discriminatorField'])) {
765 33
            $discriminatorField = $referenceMapping['discriminatorField'];
766 33
            if (isset($referenceMapping['discriminatorMap'])) {
767 8
                $pos = array_search($class->name, $referenceMapping['discriminatorMap']);
768 8
                if ($pos !== false) {
769 8
                    $discriminatorValue = $pos;
770
                }
771
            } else {
772 33
                $discriminatorValue = $class->name;
773
            }
774
        } else {
775 184
            $discriminatorField = $class->discriminatorField;
776 184
            $discriminatorValue = $class->discriminatorValue;
777
        }
778
779 203
        if ($discriminatorField !== null) {
780 44
            if ($discriminatorValue === null) {
781 4
                throw MappingException::unlistedClassInDiscriminatorMap($class->name);
782
            }
783 42
            $discriminatorData = [$discriminatorField => $discriminatorValue];
784 173
        } elseif (! isset($referenceMapping['targetDocument'])) {
785
            $discriminatorField = $referenceMapping['discriminatorField'];
786
787
            $discriminatorMap = null;
788
            if (isset($referenceMapping['discriminatorMap'])) {
789
                $discriminatorMap = $referenceMapping['discriminatorMap'];
790
            }
791
            if ($discriminatorMap === null) {
792
                $discriminatorValue = $class->name;
793
            } else {
794
                $discriminatorValue = array_search($class->name, $discriminatorMap);
795
796
                if ($discriminatorValue === false) {
797
                    throw MappingException::unlistedClassInDiscriminatorMap($class->name);
798
                }
799
            }
800
            $discriminatorData = [$discriminatorField => $discriminatorValue];
801
        }
802
803 201
        return $discriminatorData;
804
    }
805
806
    /**
807
     * Throws an exception if the DocumentManager is closed or currently not active.
808
     *
809
     * @throws MongoDBException If the DocumentManager is closed.
810
     */
811 628
    private function errorIfClosed() : void
812
    {
813 628
        if ($this->closed) {
814 5
            throw MongoDBException::documentManagerClosed();
815
        }
816 623
    }
817
818
    /**
819
     * Check if the Document manager is open or closed.
820
     */
821 1
    public function isOpen() : bool
822
    {
823 1
        return ! $this->closed;
824
    }
825
826
    /**
827
     * Gets the filter collection.
828
     */
829 529
    public function getFilterCollection() : FilterCollection
830
    {
831 529
        if ($this->filterCollection === null) {
832 529
            $this->filterCollection = new FilterCollection($this);
833
        }
834
835 529
        return $this->filterCollection;
836
    }
837
838 1651
    private function checkTypeMap() : void
839
    {
840 1651
        $typeMap = $this->client->getTypeMap();
841
842 1651
        foreach (self::CLIENT_TYPEMAP as $part => $expectedType) {
843 1651
            if (! isset($typeMap[$part]) || $typeMap[$part] !== $expectedType) {
844 3
                throw MongoDBException::invalidTypeMap($part, $expectedType);
845
            }
846
        }
847 1651
    }
848
}
849