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

DocumentManager::isOpen()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
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, []);
0 ignored issues
show
Documentation introduced by
$document is of type object<ProxyManager\Proxy\GhostObjectInterface>, but the function expects a object<Doctrine\ODM\MongoDB\object>.

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...
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
Bug introduced by
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
Bug introduced by
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;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
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
Bug introduced by
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);
0 ignored issues
show
Compatibility introduced by
$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...
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;
0 ignored issues
show
Unused Code introduced by
$discriminatorField is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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