Completed
Pull Request — master (#1667)
by Julien
08:32
created

DocumentManager::getEventManager()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

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
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ODM\MongoDB;
21
22
use Doctrine\Common\EventManager;
23
use Doctrine\Common\Persistence\ObjectManager;
24
use Doctrine\Common\Persistence\ObjectRepository;
25
use Doctrine\MongoDB\Connection;
26
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo;
27
use Doctrine\ODM\MongoDB\Mapping\MappingException;
28
use Doctrine\ODM\MongoDB\Hydrator\HydratorFactory;
29
use Doctrine\ODM\MongoDB\Proxy\ProxyFactory;
30
use Doctrine\ODM\MongoDB\Query\FilterCollection;
31
use Doctrine\ODM\MongoDB\Repository\RepositoryFactory;
32
33
/**
34
 * The DocumentManager class is the central access point for managing the
35
 * persistence of documents.
36
 *
37
 *     <?php
38
 *
39
 *     $config = new Configuration();
40
 *     $dm = DocumentManager::create(new Connection(), $config);
41
 *
42
 * @since       1.0
43
 */
44
class DocumentManager implements ObjectManager
45
{
46
    /**
47
     * The Doctrine MongoDB connection instance.
48
     *
49
     * @var \Doctrine\MongoDB\Connection
50
     */
51
    private $connection;
52
53
    /**
54
     * The used Configuration.
55
     *
56
     * @var \Doctrine\ODM\MongoDB\Configuration
57
     */
58
    private $config;
59
60
    /**
61
     * The metadata factory, used to retrieve the ODM metadata of document classes.
62
     *
63
     * @var \Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory
64
     */
65
    private $metadataFactory;
66
67
    /**
68
     * The UnitOfWork used to coordinate object-level transactions.
69
     *
70
     * @var UnitOfWork
71
     */
72
    private $unitOfWork;
73
74
    /**
75
     * The event manager that is the central point of the event system.
76
     *
77
     * @var \Doctrine\Common\EventManager
78
     */
79
    private $eventManager;
80
81
    /**
82
     * The Hydrator factory instance.
83
     *
84
     * @var HydratorFactory
85
     */
86
    private $hydratorFactory;
87
88
    /**
89
     * The Proxy factory instance.
90
     *
91
     * @var ProxyFactory
92
     */
93
    private $proxyFactory;
94
95
    /**
96
     * The repository factory used to create dynamic repositories.
97
     *
98
     * @var RepositoryFactory
99
     */
100
    private $repositoryFactory;
101
102
    /**
103
     * SchemaManager instance
104
     *
105
     * @var SchemaManager
106
     */
107
    private $schemaManager;
108
109
    /**
110
     * Array of cached document database instances that are lazily loaded.
111
     *
112
     * @var array
113
     */
114
    private $documentDatabases = array();
115
116
    /**
117
     * Array of cached document collection instances that are lazily loaded.
118
     *
119
     * @var array
120
     */
121
    private $documentCollections = array();
122
123
    /**
124
     * Whether the DocumentManager is closed or not.
125
     *
126
     * @var bool
127
     */
128
    private $closed = false;
129
130
    /**
131
     * Collection of query filters.
132
     *
133
     * @var \Doctrine\ODM\MongoDB\Query\FilterCollection
134
     */
135
    private $filterCollection;
136
137
    /**
138
     * Creates a new Document that operates on the given Mongo connection
139
     * and uses the given Configuration.
140
     *
141
     * @param \Doctrine\MongoDB\Connection|null $conn
142
     * @param Configuration|null $config
143
     * @param \Doctrine\Common\EventManager|null $eventManager
144
     */
145 1143
    protected function __construct(Connection $conn = null, Configuration $config = null, EventManager $eventManager = null)
146
    {
147 1143
        $this->config = $config ?: new Configuration();
148 1143
        $this->eventManager = $eventManager ?: new EventManager();
149 1143
        $this->connection = $conn ?: new Connection(null, array(), $this->config, $this->eventManager);
150
151 1143
        $metadataFactoryClassName = $this->config->getClassMetadataFactoryName();
152 1143
        $this->metadataFactory = new $metadataFactoryClassName();
153 1143
        $this->metadataFactory->setDocumentManager($this);
154 1143
        $this->metadataFactory->setConfiguration($this->config);
155 1143
        if ($cacheDriver = $this->config->getMetadataCacheImpl()) {
156
            $this->metadataFactory->setCacheDriver($cacheDriver);
157
        }
158
159 1143
        $hydratorDir = $this->config->getHydratorDir();
160 1143
        $hydratorNs = $this->config->getHydratorNamespace();
161 1143
        $this->hydratorFactory = new HydratorFactory(
162 1143
            $this,
163 1143
            $this->eventManager,
164 1143
            $hydratorDir,
165 1143
            $hydratorNs,
166 1143
            $this->config->getAutoGenerateHydratorClasses()
0 ignored issues
show
Bug introduced by
It seems like $this->config->getAutoGenerateHydratorClasses() targeting Doctrine\ODM\MongoDB\Con...nerateHydratorClasses() can also be of type boolean; however, Doctrine\ODM\MongoDB\Hyd...rFactory::__construct() does only seem to accept integer, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
167
        );
168
169 1143
        $this->unitOfWork = new UnitOfWork($this, $this->eventManager, $this->hydratorFactory);
170 1143
        $this->hydratorFactory->setUnitOfWork($this->unitOfWork);
171 1143
        $this->schemaManager = new SchemaManager($this, $this->metadataFactory);
172 1143
        $this->proxyFactory = new ProxyFactory($this,
173 1143
            $this->config->getProxyDir(),
174 1143
            $this->config->getProxyNamespace(),
175 1143
            $this->config->getAutoGenerateProxyClasses()
0 ignored issues
show
Bug introduced by
It seems like $this->config->getAutoGenerateProxyClasses() targeting Doctrine\ODM\MongoDB\Con...oGenerateProxyClasses() can also be of type boolean; however, Doctrine\ODM\MongoDB\Pro...yFactory::__construct() does only seem to accept integer, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
176
        );
177 1143
        $this->repositoryFactory = $this->config->getRepositoryFactory();
178 1143
    }
179
180
    /**
181
     * Gets the proxy factory used by the DocumentManager to create document proxies.
182
     *
183
     * @return ProxyFactory
184
     */
185 1
    public function getProxyFactory()
186
    {
187 1
        return $this->proxyFactory;
188
    }
189
190
    /**
191
     * Creates a new Document that operates on the given Mongo connection
192
     * and uses the given Configuration.
193
     *
194
     * @static
195
     * @param \Doctrine\MongoDB\Connection|null $conn
196
     * @param Configuration|null $config
197
     * @param \Doctrine\Common\EventManager|null $eventManager
198
     * @return DocumentManager
199
     */
200 1143
    public static function create(Connection $conn = null, Configuration $config = null, EventManager $eventManager = null)
201
    {
202 1143
        return new static($conn, $config, $eventManager);
203
    }
204
205
    /**
206
     * Gets the EventManager used by the DocumentManager.
207
     *
208
     * @return \Doctrine\Common\EventManager
209
     */
210 1193
    public function getEventManager()
211
    {
212 1193
        return $this->eventManager;
213
    }
214
215
    /**
216
     * Gets the PHP Mongo instance that this DocumentManager wraps.
217
     *
218
     * @return \Doctrine\MongoDB\Connection
219
     */
220 1149
    public function getConnection()
221
    {
222 1149
        return $this->connection;
223
    }
224
225
    /**
226
     * Gets the metadata factory used to gather the metadata of classes.
227
     *
228
     * @return \Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory
229
     */
230 1143
    public function getMetadataFactory()
231
    {
232 1143
        return $this->metadataFactory;
233
    }
234
235
    /**
236
     * Helper method to initialize a lazy loading proxy or persistent collection.
237
     *
238
     * This method is a no-op for other objects.
239
     *
240
     * @param object $obj
241
     */
242
    public function initializeObject($obj)
243
    {
244
        $this->unitOfWork->initializeObject($obj);
245
    }
246
247
    /**
248
     * Gets the UnitOfWork used by the DocumentManager to coordinate operations.
249
     *
250
     * @return UnitOfWork
251
     */
252 1149
    public function getUnitOfWork()
253
    {
254 1149
        return $this->unitOfWork;
255
    }
256
257
    /**
258
     * Gets the Hydrator factory used by the DocumentManager to generate and get hydrators
259
     * for each type of document.
260
     *
261
     * @return HydratorFactory
262
     */
263 74
    public function getHydratorFactory()
264
    {
265 74
        return $this->hydratorFactory;
266
    }
267
268
    /**
269
     * Returns SchemaManager, used to create/drop indexes/collections/databases.
270
     *
271
     * @return \Doctrine\ODM\MongoDB\SchemaManager
272
     */
273 56
    public function getSchemaManager()
274
    {
275 56
        return $this->schemaManager;
276
    }
277
278
    /**
279
     * Returns the metadata for a class.
280
     *
281
     * @param string $className The class name.
282
     * @return \Doctrine\ODM\MongoDB\Mapping\ClassMetadata
283
     * @internal Performance-sensitive method.
284
     */
285 894
    public function getClassMetadata($className)
286
    {
287 894
        return $this->metadataFactory->getMetadataFor(ltrim($className, '\\'));
288
    }
289
290
    /**
291
     * Returns the MongoDB instance for a class.
292
     *
293
     * @param string $className The class name.
294
     * @return \Doctrine\MongoDB\Database
295
     */
296 828
    public function getDocumentDatabase($className)
297
    {
298 828
        $className = ltrim($className, '\\');
299
300 828
        if (isset($this->documentDatabases[$className])) {
301 228
            return $this->documentDatabases[$className];
302
        }
303
304 817
        $metadata = $this->metadataFactory->getMetadataFor($className);
305 817
        $db = $metadata->getDatabase();
306 817
        $db = $db ?: $this->config->getDefaultDB();
307 817
        $db = $db ?: 'doctrine';
308 817
        $this->documentDatabases[$className] = $this->connection->selectDatabase($db);
309
310 817
        return $this->documentDatabases[$className];
311
    }
312
313
    /**
314
     * Gets the array of instantiated document database instances.
315
     *
316
     * @return array
317
     */
318
    public function getDocumentDatabases()
319
    {
320
        return $this->documentDatabases;
321
    }
322
323
    /**
324
     * Returns the MongoCollection instance for a class.
325
     *
326
     * @param string $className The class name.
327
     * @throws MongoDBException When the $className param is not mapped to a collection
328
     * @return \Doctrine\MongoDB\Collection
329
     */
330 824
    public function getDocumentCollection($className)
331
    {
332 824
        $className = ltrim($className, '\\');
333
334 824
        $metadata = $this->metadataFactory->getMetadataFor($className);
335 824
        $collectionName = $metadata->getCollection();
336
337 824
        if ( ! $collectionName) {
338
            throw MongoDBException::documentNotMappedToCollection($className);
339
        }
340
341 824
        if ( ! isset($this->documentCollections[$className])) {
342 813
            $db = $this->getDocumentDatabase($className);
343
344 813
            $this->documentCollections[$className] = $metadata->isFile()
345 13
                ? $db->getGridFS($collectionName)
346 806
                : $db->selectCollection($collectionName);
347
        }
348
349 824
        $collection = $this->documentCollections[$className];
350
351 824
        if ($metadata->slaveOkay !== null) {
0 ignored issues
show
Bug introduced by
Accessing slaveOkay 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...
352 2
            $collection->setSlaveOkay($metadata->slaveOkay);
0 ignored issues
show
Bug introduced by
Accessing slaveOkay 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...
353
        }
354
355 824
        if ($metadata->readPreference !== null) {
0 ignored issues
show
Bug introduced by
Accessing readPreference 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...
356 3
            $collection->setReadPreference($metadata->readPreference, $metadata->readPreferenceTags);
0 ignored issues
show
Bug introduced by
Accessing readPreference 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...
Bug introduced by
Accessing readPreferenceTags 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...
357
        }
358
359 824
        return $this->documentCollections[$className];
360
    }
361
362
    /**
363
     * Gets the array of instantiated document collection instances.
364
     *
365
     * @return array
366
     */
367
    public function getDocumentCollections()
368
    {
369
        return $this->documentCollections;
370
    }
371
372
    /**
373
     * Create a new Query instance for a class.
374
     *
375
     * @param string $documentName The document class name.
376
     * @return Query\Builder
377
     */
378 234
    public function createQueryBuilder($documentName = null)
379
    {
380 234
        return new Query\Builder($this, $documentName);
381
    }
382
383
    /**
384
     * Creates a new aggregation builder instance for a class.
385
     *
386
     * @param string $documentName The document class name.
387
     * @return Aggregation\Builder
388
     */
389 31
    public function createAggregationBuilder($documentName)
390
    {
391 31
        return new Aggregation\Builder($this, $documentName);
392
    }
393
394
    /**
395
     * Tells the DocumentManager to make an instance managed and persistent.
396
     *
397
     * The document will be entered into the database at or before transaction
398
     * commit or as a result of the flush operation.
399
     *
400
     * NOTE: The persist operation always considers documents that are not yet known to
401
     * this DocumentManager as NEW. Do not pass detached documents to the persist operation.
402
     *
403
     * @param object $document The instance to make managed and persistent.
404
     * @throws \InvalidArgumentException When the given $document param is not an object
405
     */
406 662 View Code Duplication
    public function persist($document)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
407
    {
408 662
        if ( ! is_object($document)) {
409 1
            throw new \InvalidArgumentException(gettype($document));
410
        }
411 661
        $this->errorIfClosed();
412 660
        $this->unitOfWork->persist($document);
413 656
    }
414
415
    /**
416
     * Removes a document instance.
417
     *
418
     * A removed document will be removed from the database at or before transaction commit
419
     * or as a result of the flush operation.
420
     *
421
     * @param object $document The document instance to remove.
422
     * @throws \InvalidArgumentException when the $document param is not an object
423
     */
424 26 View Code Duplication
    public function remove($document)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
425
    {
426 26
        if ( ! is_object($document)) {
427 1
            throw new \InvalidArgumentException(gettype($document));
428
        }
429 25
        $this->errorIfClosed();
430 24
        $this->unitOfWork->remove($document);
431 24
    }
432
433
    /**
434
     * Refreshes the persistent state of a document from the database,
435
     * overriding any local changes that have not yet been persisted.
436
     *
437
     * @param object $document The document to refresh.
438
     * @throws \InvalidArgumentException When the given $document param is not an object
439
     */
440 25 View Code Duplication
    public function refresh($document)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
441
    {
442 25
        if ( ! is_object($document)) {
443 1
            throw new \InvalidArgumentException(gettype($document));
444
        }
445 24
        $this->errorIfClosed();
446 23
        $this->unitOfWork->refresh($document);
447 22
    }
448
449
    /**
450
     * Detaches a document from the DocumentManager, causing a managed document to
451
     * become detached.  Unflushed changes made to the document if any
452
     * (including removal of the document), will not be synchronized to the database.
453
     * Documents which previously referenced the detached document will continue to
454
     * reference it.
455
     *
456
     * @param object $document The document to detach.
457
     * @throws \InvalidArgumentException when the $document param is not an object
458
     */
459 12
    public function detach($document)
460
    {
461 12
        if ( ! is_object($document)) {
462 1
            throw new \InvalidArgumentException(gettype($document));
463
        }
464 11
        $this->unitOfWork->detach($document);
465 11
    }
466
467
    /**
468
     * Merges the state of a detached document into the persistence context
469
     * of this DocumentManager and returns the managed copy of the document.
470
     * The document passed to merge will not become associated/managed with this DocumentManager.
471
     *
472
     * @param object $document The detached document to merge into the persistence context.
473
     * @throws LockException
474
     * @throws \InvalidArgumentException if the $document param is not an object
475
     * @return object The managed copy of the document.
476
     */
477 17 View Code Duplication
    public function merge($document)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
478
    {
479 17
        if ( ! is_object($document)) {
480 1
            throw new \InvalidArgumentException(gettype($document));
481
        }
482 16
        $this->errorIfClosed();
483 15
        return $this->unitOfWork->merge($document);
484
    }
485
486
    /**
487
     * Acquire a lock on the given document.
488
     *
489
     * @param object $document
490
     * @param int $lockMode
491
     * @param int $lockVersion
492
     * @throws \InvalidArgumentException
493
     */
494 9
    public function lock($document, $lockMode, $lockVersion = null)
495
    {
496 9
        if ( ! is_object($document)) {
497
            throw new \InvalidArgumentException(gettype($document));
498
        }
499 9
        $this->unitOfWork->lock($document, $lockMode, $lockVersion);
500 6
    }
501
502
    /**
503
     * Releases a lock on the given document.
504
     *
505
     * @param object $document
506
     * @throws \InvalidArgumentException if the $document param is not an object
507
     */
508 1
    public function unlock($document)
509
    {
510 1
        if ( ! is_object($document)) {
511
            throw new \InvalidArgumentException(gettype($document));
512
        }
513 1
        $this->unitOfWork->unlock($document);
514 1
    }
515
516
    /**
517
     * Gets the repository for a document class.
518
     *
519
     * @param string $documentName  The name of the Document.
520
     * @return ObjectRepository  The repository.
521
     */
522 373
    public function getRepository($documentName)
523
    {
524 373
        return $this->repositoryFactory->getRepository($this, $documentName);
525
    }
526
527
    /**
528
     * Flushes all changes to objects that have been queued up to now to the database.
529
     * This effectively synchronizes the in-memory state of managed objects with the
530
     * database.
531
     *
532
     * @param object $document
533
     * @param array $options Array of options to be used with batchInsert(), update() and remove()
534
     * @throws \InvalidArgumentException
535
     */
536 632
    public function flush($document = null, array $options = array())
537
    {
538 632
        if (null !== $document && ! is_object($document) && ! is_array($document)) {
539
            throw new \InvalidArgumentException(gettype($document));
540
        }
541 632
        $this->errorIfClosed();
542 631
        $this->unitOfWork->commit($document, $options);
0 ignored issues
show
Bug introduced by
It seems like $document can also be of type array; however, Doctrine\ODM\MongoDB\UnitOfWork::commit() does only seem to accept object|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
543 626
    }
544
545
    /**
546
     * Gets a reference to the document identified by the given type and identifier
547
     * without actually loading it.
548
     *
549
     * If partial objects are allowed, this method will return a partial object that only
550
     * has its identifier populated. Otherwise a proxy is returned that automatically
551
     * loads itself on first access.
552
     *
553
     * @param string $documentName
554
     * @param string|object $identifier
555
     * @return mixed|object The document reference.
556
     */
557 123
    public function getReference($documentName, $identifier)
558
    {
559
        /* @var $class \Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo */
560 123
        $class = $this->metadataFactory->getMetadataFor(ltrim($documentName, '\\'));
561
562
        // Check identity map first, if its already in there just return it.
563 123
        if ($document = $this->unitOfWork->tryGetById($identifier, $class)) {
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\ODM\Mong...ping\ClassMetadataInfo> is not a sub-type of object<Doctrine\ODM\Mong...\Mapping\ClassMetadata>. It seems like you assume a child class of the class Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo 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...
564 48
            return $document;
565
        }
566
567 97
        $document = $this->proxyFactory->getProxy($class->name, array($class->identifier => $identifier));
568 97
        $this->unitOfWork->registerManaged($document, $identifier, array());
0 ignored issues
show
Documentation introduced by
$identifier is of type string|object, 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...
569
570 97
        return $document;
571
    }
572
573
    /**
574
     * Gets a partial reference to the document identified by the given type and identifier
575
     * without actually loading it, if the document is not yet loaded.
576
     *
577
     * The returned reference may be a partial object if the document is not yet loaded/managed.
578
     * If it is a partial object it will not initialize the rest of the document state on access.
579
     * Thus you can only ever safely access the identifier of a document obtained through
580
     * this method.
581
     *
582
     * The use-cases for partial references involve maintaining bidirectional associations
583
     * without loading one side of the association or to update a document without loading it.
584
     * Note, however, that in the latter case the original (persistent) document data will
585
     * never be visible to the application (especially not event listeners) as it will
586
     * never be loaded in the first place.
587
     *
588
     * @param string $documentName The name of the document type.
589
     * @param mixed $identifier The document identifier.
590
     * @return object The (partial) document reference.
591
     */
592 1
    public function getPartialReference($documentName, $identifier)
593
    {
594 1
        $class = $this->metadataFactory->getMetadataFor(ltrim($documentName, '\\'));
595
596
        // Check identity map first, if its already in there just return it.
597 1
        if ($document = $this->unitOfWork->tryGetById($identifier, $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...
598
            return $document;
599
        }
600 1
        $document = $class->newInstance();
601 1
        $class->setIdentifierValue($document, $identifier);
0 ignored issues
show
Bug introduced by
The method setIdentifierValue() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. Did you maybe mean getIdentifierValues()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
602 1
        $this->unitOfWork->registerManaged($document, $identifier, array());
603
604 1
        return $document;
605
    }
606
607
    /**
608
     * Finds a Document by its identifier.
609
     *
610
     * This is just a convenient shortcut for getRepository($documentName)->find($id).
611
     *
612
     * @param string $documentName
613
     * @param mixed $identifier
614
     * @param int $lockMode
615
     * @param int $lockVersion
616
     * @return object $document
617
     */
618 193
    public function find($documentName, $identifier, $lockMode = LockMode::NONE, $lockVersion = null)
619
    {
620 193
        return $this->getRepository($documentName)->find($identifier, $lockMode, $lockVersion);
0 ignored issues
show
Unused Code introduced by
The call to ObjectRepository::find() has too many arguments starting with $lockMode.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
621
    }
622
623
    /**
624
     * Clears the DocumentManager.
625
     *
626
     * All documents that are currently managed by this DocumentManager become
627
     * detached.
628
     *
629
     * @param string|null $documentName if given, only documents of this type will get detached
630
     */
631 425
    public function clear($documentName = null)
632
    {
633 425
        $this->unitOfWork->clear($documentName);
634 425
    }
635
636
    /**
637
     * Closes the DocumentManager. All documents that are currently managed
638
     * by this DocumentManager become detached. The DocumentManager may no longer
639
     * be used after it is closed.
640
     */
641 6
    public function close()
642
    {
643 6
        $this->clear();
644 6
        $this->closed = true;
645 6
    }
646
647
    /**
648
     * Determines whether a document instance is managed in this DocumentManager.
649
     *
650
     * @param object $document
651
     * @throws \InvalidArgumentException When the $document param is not an object
652
     * @return boolean TRUE if this DocumentManager currently manages the given document, FALSE otherwise.
653
     */
654 6
    public function contains($document)
655
    {
656 6
        if ( ! is_object($document)) {
657
            throw new \InvalidArgumentException(gettype($document));
658
        }
659 6
        return $this->unitOfWork->isScheduledForInsert($document) ||
660 6
            $this->unitOfWork->isInIdentityMap($document) &&
661 6
            ! $this->unitOfWork->isScheduledForDelete($document);
662
    }
663
664
    /**
665
     * Gets the Configuration used by the DocumentManager.
666
     *
667
     * @return Configuration
668
     */
669 805
    public function getConfiguration()
670
    {
671 805
        return $this->config;
672
    }
673
674
    /**
675
     * Returns a reference to the supplied document.
676
     *
677
     * @param object $document A document object
678
     * @param array $referenceMapping Mapping for the field that references the document
679
     *
680
     * @throws \InvalidArgumentException
681
     * @return mixed The reference for the document in question, according to the desired mapping
682
     */
683 243
    public function createReference($document, array $referenceMapping)
684
    {
685 243
        if ( ! is_object($document)) {
686
            throw new \InvalidArgumentException('Cannot create a DBRef, the document is not an object');
687
        }
688
689 243
        $class = $this->getClassMetadata(get_class($document));
690 243
        $id = $this->unitOfWork->getDocumentIdentifier($document);
691
692 243
        if ($id === null) {
693 1
            throw new \RuntimeException(
694 1
                sprintf('Cannot create a DBRef for class %s without an identifier. Have you forgotten to persist/merge the document first?', $class->name)
695
            );
696
        }
697
698 242
        $storeAs = isset($referenceMapping['storeAs']) ? $referenceMapping['storeAs'] : null;
699 242
        switch ($storeAs) {
700
            case ClassMetadataInfo::REFERENCE_STORE_AS_ID:
701 42
                if ($class->inheritanceType === ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_COLLECTION) {
702 1
                    throw MappingException::simpleReferenceMustNotTargetDiscriminatedDocument($referenceMapping['targetDocument']);
703
                }
704
705 41
                return $class->getDatabaseIdentifierValue($id);
706
                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...
707
708
709
            case ClassMetadataInfo::REFERENCE_STORE_AS_REF:
710 14
                $reference = ['id' => $class->getDatabaseIdentifierValue($id)];
711 14
                break;
712
713 View Code Duplication
            case ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
714
                $reference = [
715 11
                    '$ref' => $class->getCollection(),
716 11
                    '$id'  => $class->getDatabaseIdentifierValue($id),
717
                ];
718 11
                break;
719
720 View Code Duplication
            case ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF_WITH_DB:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
721
                $reference = [
722 200
                    '$ref' => $class->getCollection(),
723 200
                    '$id'  => $class->getDatabaseIdentifierValue($id),
724 200
                    '$db'  => $this->getDocumentDatabase($class->name)->getName(),
725
                ];
726 200
                break;
727
728
            default:
729
                throw new \InvalidArgumentException("Reference type {$storeAs} is invalid.");
730
        }
731
732
        /* If the class has a discriminator (field and value), use it. A child
733
         * class that is not defined in the discriminator map may only have a
734
         * discriminator field and no value, so default to the full class name.
735
         */
736 220
        if (isset($class->discriminatorField)) {
737 18
            $reference[$class->discriminatorField] = isset($class->discriminatorValue)
738 16
                ? $class->discriminatorValue
739 4
                : $class->name;
740
        }
741
742
        /* Add a discriminator value if the referenced document is not mapped
743
         * explicitly to a targetDocument class.
744
         */
745 220 View Code Duplication
        if (! isset($referenceMapping['targetDocument'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
746 33
            $discriminatorField = $referenceMapping['discriminatorField'];
747 33
            $discriminatorValue = isset($referenceMapping['discriminatorMap'])
748 8
                ? array_search($class->name, $referenceMapping['discriminatorMap'])
749 33
                : $class->name;
750
751
            /* If the discriminator value was not found in the map, use the full
752
             * class name. In the future, it may be preferable to throw an
753
             * exception here (perhaps based on some strictness option).
754
             *
755
             * @see PersistenceBuilder::prepareEmbeddedDocumentValue()
756
             */
757 33
            if ($discriminatorValue === false) {
758 2
                $discriminatorValue = $class->name;
759
            }
760
761 33
            $reference[$discriminatorField] = $discriminatorValue;
762
        }
763
764 220
        return $reference;
765
    }
766
767
    /**
768
     * Returns a DBRef array for the supplied document.
769
     *
770
     * @param mixed $document A document object
771
     * @param array $referenceMapping Mapping for the field that references the document
772
     *
773
     * @throws \InvalidArgumentException
774
     * @return array A DBRef array
775
     * @deprecated Deprecated in favor of createReference; will be removed in 2.0
776
     */
777
    public function createDBRef($document, array $referenceMapping = null)
778
    {
779
        @trigger_error('The ' . __METHOD__ . ' method has been deprecated and will be removed in ODM 2.0. Use createReference() instead.', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
780
781
        if (!isset($referenceMapping['storeAs'])) {
782
            $referenceMapping['storeAs'] = ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF;
783
        }
784
785
        return $this->createReference($document, $referenceMapping);
786
    }
787
788
    /**
789
     * Throws an exception if the DocumentManager is closed or currently not active.
790
     *
791
     * @throws MongoDBException If the DocumentManager is closed.
792
     */
793 670
    private function errorIfClosed()
794
    {
795 670
        if ($this->closed) {
796 5
            throw MongoDBException::documentManagerClosed();
797
        }
798 665
    }
799
800
    /**
801
     * Check if the Document manager is open or closed.
802
     *
803
     * @return bool
804
     */
805 1
    public function isOpen()
806
    {
807 1
        return ( ! $this->closed);
808
    }
809
810
    /**
811
     * Gets the filter collection.
812
     *
813
     * @return \Doctrine\ODM\MongoDB\Query\FilterCollection The active filter collection.
814
     */
815 555
    public function getFilterCollection()
816
    {
817 555
        if (null === $this->filterCollection) {
818 555
            $this->filterCollection = new FilterCollection($this);
819
        }
820
821 555
        return $this->filterCollection;
822
    }
823
}
824