Failed Conditions
Pull Request — master (#7143)
by Mike
09:42
created

EntityManager::close()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 5
ccs 2
cts 2
cp 1
crap 1
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM;
6
7
use Doctrine\Common\EventManager;
8
use Doctrine\Common\Persistence\Mapping\MappingException;
9
use Doctrine\Common\Persistence\ObjectRepository;
10
use Doctrine\DBAL\Connection;
11
use Doctrine\DBAL\DriverManager;
12
use Doctrine\DBAL\LockMode;
13
use Doctrine\ORM\Mapping\ClassMetadataFactory;
14
use Doctrine\ORM\Mapping\MetadataCollection;
15
use Doctrine\ORM\Proxy\Factory\ProxyFactory;
16
use Doctrine\ORM\Proxy\Factory\StaticProxyFactory;
17
use Doctrine\ORM\Query\Expr;
18
use Doctrine\ORM\Query\FilterCollection;
19
use Doctrine\ORM\Query\ResultSetMapping;
20
use Doctrine\ORM\Repository\RepositoryFactory;
21
use Doctrine\ORM\Utility\IdentifierFlattener;
22
use Doctrine\ORM\Utility\StaticClassNameConverter;
23
use function array_keys;
24
use function get_class;
25
use function gettype;
26
use function is_array;
27
use function is_object;
28
use function ltrim;
29
use function sprintf;
30
31
/**
32
 * The EntityManager is the central access point to ORM functionality.
33
 *
34
 * It is a facade to all different ORM subsystems such as UnitOfWork,
35
 * Query Language and Repository API. Instantiation is done through
36
 * the static create() method. The quickest way to obtain a fully
37
 * configured EntityManager is:
38
 *
39
 *     use Doctrine\ORM\Tools\Setup;
40
 *     use Doctrine\ORM\EntityManager;
41
 *
42
 *     $paths = array('/path/to/entity/mapping/files');
43
 *
44
 *     $config = Setup::createAnnotationMetadataConfiguration($paths);
45
 *     $dbParams = array('driver' => 'pdo_sqlite', 'memory' => true);
46
 *     $entityManager = EntityManager::create($dbParams, $config);
47
 *
48
 * For more information see
49
 * {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html}
50
 *
51
 * You should never attempt to inherit from the EntityManager: Inheritance
52
 * is not a valid extension point for the EntityManager. Instead you
53
 * should take a look at the {@see \Doctrine\ORM\Decorator\EntityManagerDecorator}
54
 * and wrap your entity manager in a decorator.
55
 */
56
final class EntityManager implements EntityManagerInterface
57
{
58
    /**
59
     * The used Configuration.
60
     *
61
     * @var Configuration
62
     */
63
    private $config;
64
65
    /**
66
     * The database connection used by the EntityManager.
67
     *
68
     * @var Connection
69
     */
70
    private $conn;
71
72
    /**
73
     * The UnitOfWork used to coordinate object-level transactions.
74
     *
75
     * @var UnitOfWork
76
     */
77
    private $unitOfWork;
78
79
    /**
80
     * The event manager that is the central point of the event system.
81
     *
82
     * @var EventManager
83
     */
84
    private $eventManager;
85
86
    /**
87
     * The proxy factory used to create dynamic proxies.
88
     *
89
     * @var ProxyFactory
90
     */
91
    private $proxyFactory;
92
93
    /**
94
     * The repository factory used to create dynamic repositories.
95
     *
96
     * @var RepositoryFactory
97
     */
98
    private $repositoryFactory;
99
100
    /**
101
     * The expression builder instance used to generate query expressions.
102
     *
103
     * @var Expr
104
     */
105
    private $expressionBuilder;
106
107
    /**
108
     * The IdentifierFlattener used for manipulating identifiers
109
     *
110
     * @var IdentifierFlattener
111
     */
112
    private $identifierFlattener;
113
114
    /**
115
     * Whether the EntityManager is closed or not.
116
     *
117
     * @var bool
118
     */
119
    private $closed = false;
120
121
    /**
122
     * Collection of query filters.
123
     *
124
     * @var FilterCollection
125
     */
126
    private $filterCollection;
127
128
    /** @var Cache The second level cache regions API. */
129
    private $cache;
130
131
    /** @var MetadataCollection */
132
    private $metadatas;
133
134
    /**
135
     * Creates a new EntityManager that operates on the given database connection
136
     * and uses the given Configuration and EventManager implementations.
137
     *
138
     */
139
    protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager, MetadataCollection $metadataCollection)
140
    {
141
        $this->conn            = $conn;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 9 spaces but found 12 spaces

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
142 2247
        $this->config          = $config;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 7 spaces but found 10 spaces

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
143
        $this->eventManager    = $eventManager;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 1 space but found 4 spaces

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
144 2247
        $this->metadatas       = $metadataCollection;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 7 spaces

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
145 2247
146 2247
        $this->repositoryFactory   = $config->getRepositoryFactory();
147
        $this->unitOfWork          = new UnitOfWork($this);
148 2247
        $this->proxyFactory        = new StaticProxyFactory($this, $this->config->buildGhostObjectFactory());
149
        $this->identifierFlattener = new IdentifierFlattener($this->unitOfWork, $this->metadatas);
150 2247
151
        if ($config->isSecondLevelCacheEnabled()) {
152 2247
            $cacheConfig  = $config->getSecondLevelCacheConfiguration();
0 ignored issues
show
Unused Code introduced by
The assignment to $cacheConfig is dead and can be removed.
Loading history...
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 2 spaces
Loading history...
153 2247
            //$cacheFactory = $cacheConfig->getCacheFactory();
154
            //$this->cache  = $cacheFactory->createCache($this);
155 2247
        }
156 2247
    }
157 2247
158 2247
159
160 2247
    /**
161 280
     * {@inheritDoc}
162 280
     */
163 280
    public function getConnection()
164
    {
165 2247
        return $this->conn;
166
    }
167
168
    /**
169
     * {@inheritDoc}
170 1781
     */
171
    public function getExpressionBuilder()
172 1781
    {
173
        if ($this->expressionBuilder === null) {
174
            $this->expressionBuilder = new Query\Expr();
175
        }
176
177
        return $this->expressionBuilder;
178
    }
179
180 812
    public function getIdentifierFlattener() : IdentifierFlattener
181
    {
182 812
        return $this->identifierFlattener;
183
    }
184
185
    /**
186
     * {@inheritDoc}
187
     */
188 17
    public function beginTransaction()
189
    {
190 17
        $this->conn->beginTransaction();
191 17
    }
192
193
    /**
194 17
     * {@inheritDoc}
195
     */
196
    public function getCache()
197 1094
    {
198
        return $this->cache;
199 1094
    }
200
201
    /**
202
     * {@inheritDoc}
203
     */
204
    public function transactional(callable $func)
205 1
    {
206
        $this->conn->beginTransaction();
207 1
208 1
        try {
209
            $return = $func($this);
210
211
            $this->flush();
212
            $this->conn->commit();
213 212
214
            return $return;
215 212
        } catch (\Throwable $e) {
216
            $this->close();
217
            $this->conn->rollBack();
218
219
            throw $e;
220
        }
221 6
    }
222
223 6
    /**
224
     * {@inheritDoc}
225
     */
226 6
    public function commit()
227
    {
228 5
        $this->conn->commit();
229 5
    }
230
231 5
    /**
232 1
     * {@inheritDoc}
233 1
     */
234 1
    public function rollback()
235
    {
236 1
        $this->conn->rollBack();
237
    }
238
239
    /**
240
     * Returns the ORM metadata descriptor for a class.
241
     *
242
     * The class name must be the fully-qualified class name without a leading backslash
243 1
     * (as it is returned by get_class($obj)) or an aliased class name.
244
     *
245 1
     * Examples:
246 1
     * MyProject\Domain\User
247
     * sales:PriceRequest
248
     *
249
     * {@internal Performance-sensitive method. }}
250
     *
251
     * @param string $className
252
     *
253
     * @throws \ReflectionException
254
     * @throws \InvalidArgumentException
255
     * @throws MappingException
256
     */
257
    public function getClassMetadata($className) : Mapping\ClassMetadata
258
    {
259
        return $this->metadatas->get($className);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->metadatas->get($className) returns the type Doctrine\ORM\Mapping\ClassMetadata which is incompatible with the return type mandated by Doctrine\Common\Persiste...ger::getClassMetadata() of Doctrine\Common\Persistence\Mapping\ClassMetadata.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
260
    }
261
262
    /**
263
     * {@inheritDoc}
264
     */
265
    public function createQuery($dql = '')
266
    {
267
        $query = new Query($this);
268
269
        if (! empty($dql)) {
270
            $query->setDQL($dql);
271
        }
272
273
        return $query;
274 1894
    }
275
276 1894
    /**
277
     * {@inheritDoc}
278
     */
279
    public function createNativeQuery($sql, ResultSetMapping $rsm)
280
    {
281
        $query = new NativeQuery($this);
282 954
283
        $query->setSQL($sql);
284 954
        $query->setResultSetMapping($rsm);
285
286 954
        return $query;
287 949
    }
288
289
    /**
290 954
     * {@inheritDoc}
291
     */
292
    public function createQueryBuilder()
293
    {
294
        return new QueryBuilder($this);
295
    }
296 13
297
    /**
298 13
     * {@inheritDoc}
299
     *
300 13
     * @deprecated
301 13
     */
302
    public function merge($object)
303 13
    {
304
        throw new \BadMethodCallException('@TODO method disabled - will be removed in 3.0 with a release of doctrine/common');
305
    }
306
307
    /**
308
     * {@inheritDoc}
309 128
     *
310
     * @deprecated
311 128
     */
312
    public function detach($object)
313
    {
314
        throw new \BadMethodCallException('@TODO method disabled - will be removed in 3.0 with a release of doctrine/common');
315
    }
316
317
    /**
318
     * Flushes all changes to objects that have been queued up to now to the database.
319
     * This effectively synchronizes the in-memory state of managed objects with the
320
     * database.
321
     *
322
     * If an entity is explicitly passed to this method only this entity and
323
     * the cascade-persist semantics + scheduled inserts/removals are synchronized.
324
     *
325
     * @throws OptimisticLockException If a version check on an entity that
326
     *         makes use of optimistic locking fails.
327
     * @throws ORMException
328
     */
329
    public function flush()
330
    {
331
        $this->errorIfClosed();
332
333
        $this->unitOfWork->commit();
334
    }
335
336
    /**
337
     * Finds an Entity by its identifier.
338
     *
339
     * @param string   $entityName  The class name of the entity to find.
340
     * @param mixed    $id          The identity of the entity to find.
341
     * @param int|null $lockMode    One of the \Doctrine\DBAL\LockMode::* constants
342
     *                              or NULL if no specific lock mode should be used
343
     *                              during the search.
344
     * @param int|null $lockVersion The version of the entity to find when using
345
     *                              optimistic locking.
346 1005
     *
347
     * @return object|null The entity instance or NULL if the entity can not be found.
348 1005
     *
349
     * @throws OptimisticLockException
350 1004
     * @throws ORMInvalidArgumentException
351 994
     * @throws TransactionRequiredException
352
     * @throws ORMException
353
     */
354
    public function find($entityName, $id, $lockMode = null, $lockVersion = null)
355
    {
356
        $class     = $this->metadatas->get(ltrim($entityName, '\\'));
357
        $className = $class->getClassName();
358
359
        if (! is_array($id)) {
360
            if ($class->isIdentifierComposite()) {
361
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
362
            }
363
364
            $id = [$class->identifier[0] => $id];
365
        }
366
367
        foreach ($id as $i => $value) {
368
            if (is_object($value)) {
369
                try{
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after TRY keyword; 0 found
Loading history...
370
                    $this->metadatas->get(StaticClassNameConverter::getClass($value));
371 403
                    $id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
372
                } catch (\Exception $e) {
373 403
                    $id[$i] = null;
374 403
                }
375
376 403
                if ($id[$i] === null) {
377 371
                    throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
378
                }
379
            }
380
        }
381 371
382
        $sortedId = [];
383
384 403
        foreach ($class->identifier as $identifier) {
385 403
            if (! isset($id[$identifier])) {
386 10
                throw ORMException::missingIdentifierField($className, $identifier);
387
            }
388 10
389 403
            $sortedId[$identifier] = $id[$identifier];
390
            unset($id[$identifier]);
391
        }
392
393
        if ($id) {
394 402
            throw ORMException::unrecognizedIdentifierFields($className, array_keys($id));
395
        }
396 402
397 402
        $unitOfWork = $this->getUnitOfWork();
398 1
399
        // Check identity map first
400
        $entity = $unitOfWork->tryGetById($sortedId, $class->getRootClassName());
401 401
        if ($entity !== false) {
402 401
            if (! ($entity instanceof $className)) {
403
                return null;
404
            }
405 401
406 1
            switch (true) {
407
                case $lockMode === LockMode::OPTIMISTIC:
408
                    $this->lock($entity, $lockMode, $lockVersion);
409 400
                    break;
410
411
                case $lockMode === LockMode::NONE:
412 400
                case $lockMode === LockMode::PESSIMISTIC_READ:
413 400
                case $lockMode === LockMode::PESSIMISTIC_WRITE:
414 45
                    $persister = $unitOfWork->getEntityPersister($className);
415 1
                    $persister->refresh($sortedId, $entity, $lockMode);
0 ignored issues
show
Bug introduced by
It seems like $entity can also be of type true; however, parameter $entity of Doctrine\ORM\Persisters\...ityPersister::refresh() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

415
                    $persister->refresh($sortedId, /** @scrutinizer ignore-type */ $entity, $lockMode);
Loading history...
416
                    break;
417
            }
418
419 44
            return $entity; // Hit!
0 ignored issues
show
Bug Best Practice introduced by
The expression return $entity also could return the type true which is incompatible with the documented return type object|null.
Loading history...
420 1
        }
421
422
        $persister = $unitOfWork->getEntityPersister($className);
423 44
424 44
        switch (true) {
425 44
            case $lockMode === LockMode::OPTIMISTIC:
426
                if (! $class->isVersioned()) {
427
                    throw OptimisticLockException::notVersioned($className);
428
                }
429
430
                $entity = $persister->load($sortedId);
431 44
432
                $unitOfWork->lock($entity, $lockMode, $lockVersion);
433
434 379
                return $entity;
435
436
            case $lockMode === LockMode::PESSIMISTIC_READ:
437 379
            case $lockMode === LockMode::PESSIMISTIC_WRITE:
438 1
                if (! $this->getConnection()->isTransactionActive()) {
439 1
                    throw TransactionRequiredException::transactionRequired();
440
                }
441
442
                return $persister->load($sortedId, null, null, [], $lockMode);
443
444
            default:
445
                return $persister->loadById($sortedId);
446
        }
447
    }
448 378
449 377
    /**
450 2
     * {@inheritDoc}
451 2
     */
452
    public function getReference($entityName, $id)
453
    {
454
        $class     = $this->metadatas->get(ltrim($entityName, '\\'));
455
        $className = $class->getClassName();
456
457 376
        if (! is_array($id)) {
458
            if ($class->isIdentifierComposite()) {
459
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
460
            }
461
462
            $id = [$class->identifier[0] => $id];
463
        }
464 90
465
        $scalarId = [];
466 90
467 90
        foreach ($id as $i => $value) {
468
            $scalarId[$i] = $value;
469 90
470 39
            if (is_object($value)) {
471
                try{
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after TRY keyword; 0 found
Loading history...
472
                    $this->metadatas->get(StaticClassNameConverter::getClass($value));
473
                    $scalarId[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
474 39
                } catch (\Exception $e) {
475
                    $scalarId[$i] = null;
476
                }
477 90
478
                if ($scalarId[$i] === null) {
479 90
                    throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
480 90
                }
481
            }
482 90
        }
483 2
484
        $sortedId = [];
485 2
486 90
        foreach ($class->identifier as $identifier) {
487
            if (! isset($scalarId[$identifier])) {
488
                throw ORMException::missingIdentifierField($className, $identifier);
489
            }
490
491 90
            $sortedId[$identifier] = $scalarId[$identifier];
492
            unset($scalarId[$identifier]);
493 90
        }
494 90
495
        if ($scalarId) {
496
            throw ORMException::unrecognizedIdentifierFields($className, array_keys($scalarId));
497
        }
498 90
499 90
        // Check identity map first, if its already in there just return it.
500
        $entity = $this->unitOfWork->tryGetById($sortedId, $class->getRootClassName());
501
        if ($entity !== false) {
502 90
            return ($entity instanceof $className) ? $entity : null;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $entity instanceo...ssName ? $entity : null also could return the type true which is incompatible with the return type mandated by Doctrine\ORM\EntityManag...terface::getReference() of object|null|ProxyManager...xy\GhostObjectInterface.
Loading history...
503 1
        }
504
505
        if ($class->getSubClasses()) {
506
            return $this->find($entityName, $sortedId);
507 89
        }
508 89
509 29
        $entity = $this->proxyFactory->getProxy($class, $id);
510
511
        $this->unitOfWork->registerManaged($entity, $sortedId, []);
512 84
513 2
        if ($entity instanceof EntityManagerAware) {
514
            $entity->injectEntityManager($this, $class);
515
        }
516 84
517
        return $entity;
518 84
    }
519
520 84
    /**
521 3
     * {@inheritDoc}
522
     */
523
    public function getPartialReference($entityName, $id)
524 84
    {
525
        $class     = $this->metadatas->get(ltrim($entityName, '\\'));
526
        $className = $class->getClassName();
527
528
        if (! is_array($id)) {
529
            if ($class->isIdentifierComposite()) {
530 4
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
531
            }
532 4
533 4
            $id = [$class->identifier[0] => $id];
534
        }
535 4
536 4
        foreach ($id as $i => $value) {
537
            if (is_object($value)) {
538
                try{
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after TRY keyword; 0 found
Loading history...
539
                    $this->metadatas->get(StaticClassNameConverter::getClass($value));
540 4
                    $id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
541
                } catch (\Exception $e) {
542
                    $id[$i] = null;
543 4
                }
544 4
545
                if ($id[$i] === null) {
546
                    throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
547
                }
548 4
            }
549
        }
550
551
        $sortedId = [];
552
553 4
        foreach ($class->identifier as $identifier) {
554
            if (! isset($id[$identifier])) {
555 4
                throw ORMException::missingIdentifierField($className, $identifier);
556 4
            }
557
558
            $sortedId[$identifier] = $id[$identifier];
559
            unset($id[$identifier]);
560 4
        }
561 4
562
        if ($id) {
563
            throw ORMException::unrecognizedIdentifierFields($className, array_keys($id));
564 4
        }
565
566
        // Check identity map first, if its already in there just return it.
567
        $entity = $this->unitOfWork->tryGetById($sortedId, $class->getRootClassName());
568
        if ($entity !== false) {
569 4
            return ($entity instanceof $className) ? $entity : null;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $entity instanceo...ssName ? $entity : null also could return the type true which is incompatible with the return type mandated by Doctrine\ORM\EntityManag...::getPartialReference() of object.
Loading history...
570 4
        }
571 1
572
        $persister = $this->unitOfWork->getEntityPersister($class->getClassName());
573
        $entity    = $this->unitOfWork->newInstance($class);
574 3
575 3
        $persister->setIdentifier($entity, $sortedId);
576
577 3
        $this->unitOfWork->registerManaged($entity, $sortedId, []);
578
        $this->unitOfWork->markReadOnly($entity);
579 3
580 3
        return $entity;
581
    }
582 3
583
    /**
584
     * Clears the EntityManager. All entities that are currently managed
585
     * by this EntityManager become detached.
586
     *
587
     * @param null $entityName Unused. @todo Remove from ObjectManager.
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $entityName is correct as it would always require null to be passed?
Loading history...
588
     *
589
     */
590
    public function clear($entityName = null)
591
    {
592 1197
        $this->unitOfWork->clear();
593
594 1197
        $this->unitOfWork = new UnitOfWork($this);
595
596 1197
        if ($this->eventManager->hasListeners(Events::onClear)) {
597
            $this->eventManager->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this));
598 1197
        }
599 9
    }
600
601 1197
    /**
602
     * {@inheritDoc}
603
     */
604
    public function close()
605
    {
606 18
        $this->clear();
607
608 18
        $this->closed = true;
609
    }
610 18
611 18
    /**
612
     * Tells the EntityManager to make an instance managed and persistent.
613
     *
614
     * The entity will be entered into the database at or before transaction
615
     * commit or as a result of the flush operation.
616
     *
617
     * NOTE: The persist operation always considers entities that are not yet known to
618
     * this EntityManager as NEW. Do not pass detached entities to the persist operation.
619
     *
620
     * @param object $entity The instance to make managed and persistent.
621
     *
622
     * @throws ORMInvalidArgumentException
623
     * @throws ORMException
624
     */
625
    public function persist($entity)
626
    {
627 999
        if (! is_object($entity)) {
628
            throw ORMInvalidArgumentException::invalidObject('EntityManager#persist()', $entity);
629 999
        }
630 1
631
        $this->errorIfClosed();
632
633 998
        $this->unitOfWork->persist($entity);
634
    }
635 997
636 996
    /**
637
     * Removes an entity instance.
638
     *
639
     * A removed entity will be removed from the database at or before transaction commit
640
     * or as a result of the flush operation.
641
     *
642
     * @param object $entity The entity instance to remove.
643
     *
644
     * @throws ORMInvalidArgumentException
645
     * @throws ORMException
646
     */
647
    public function remove($entity)
648
    {
649 49
        if (! is_object($entity)) {
650
            throw ORMInvalidArgumentException::invalidObject('EntityManager#remove()', $entity);
651 49
        }
652 1
653
        $this->errorIfClosed();
654
655 48
        $this->unitOfWork->remove($entity);
656
    }
657 47
658 47
    /**
659
     * Refreshes the persistent state of an entity from the database,
660
     * overriding any local changes that have not yet been persisted.
661
     *
662
     * @param object $entity The entity to refresh.
663
     *
664
     * @throws ORMInvalidArgumentException
665
     * @throws ORMException
666
     */
667
    public function refresh($entity)
668
    {
669 17
        if (! is_object($entity)) {
670
            throw ORMInvalidArgumentException::invalidObject('EntityManager#refresh()', $entity);
671 17
        }
672 1
673
        $this->errorIfClosed();
674
675 16
        $this->unitOfWork->refresh($entity);
676
    }
677 15
678 15
    /**
679
     * {@inheritDoc}
680
     */
681
    public function lock($entity, $lockMode, $lockVersion = null)
682
    {
683 9
        $this->unitOfWork->lock($entity, $lockMode, $lockVersion);
684
    }
685 9
686 2
    /**
687
     * Gets the repository for an entity class.
688
     *
689
     * @param string $entityName The name of the entity.
690
     *
691
     * @return ObjectRepository|EntityRepository The repository class.
692
     */
693
    public function getRepository($entityName)
694
    {
695 137
        return $this->repositoryFactory->getRepository($this, $entityName);
696
    }
697 137
698
    /**
699
     * Determines whether an entity instance is managed in this EntityManager.
700
     *
701
     * @param object $entity
702
     *
703
     * @return bool TRUE if this EntityManager currently manages the given entity, FALSE otherwise.
704
     */
705
    public function contains($entity)
706
    {
707 15
        return $this->unitOfWork->isScheduledForInsert($entity)
708
            || ($this->unitOfWork->isInIdentityMap($entity) && ! $this->unitOfWork->isScheduledForDelete($entity));
709 15
    }
710 15
711
    /**
712
     * {@inheritDoc}
713
     */
714
    public function getEventManager()
715
    {
716 2247
        return $this->eventManager;
717
    }
718 2247
719
    /**
720
     * {@inheritDoc}
721
     */
722
    public function getConfiguration()
723
    {
724 2247
        return $this->config;
725
    }
726 2247
727
    /**
728
     * Throws an exception if the EntityManager is closed or currently not active.
729
     *
730
     * @throws ORMException If the EntityManager is closed.
731
     */
732
    private function errorIfClosed()
733
    {
734 1010
        if ($this->closed) {
735
            throw ORMException::entityManagerClosed();
736 1010
        }
737 4
    }
738
739 1006
    /**
740
     * {@inheritDoc}
741
     */
742
    public function isOpen()
743
    {
744 2
        return ! $this->closed;
745
    }
746 2
747
    /**
748
     * {@inheritDoc}
749
     */
750
    public function getUnitOfWork()
751
    {
752 1350
        return $this->unitOfWork;
753
    }
754 1350
755
    /**
756
     * {@inheritDoc}
757
     */
758
    public function getHydrator($hydrationMode)
759
    {
760
        return $this->newHydrator($hydrationMode);
761
    }
762
763
    /**
764
     * {@inheritDoc}
765
     */
766
    public function newHydrator($hydrationMode)
767
    {
768 883
        switch ($hydrationMode) {
769
            case Query::HYDRATE_OBJECT:
770 883
                return new Internal\Hydration\ObjectHydrator($this);
771
772 614
            case Query::HYDRATE_ARRAY:
773
                return new Internal\Hydration\ArrayHydrator($this);
774
775 45
            case Query::HYDRATE_SCALAR:
776
                return new Internal\Hydration\ScalarHydrator($this);
777
778 85
            case Query::HYDRATE_SINGLE_SCALAR:
779
                return new Internal\Hydration\SingleScalarHydrator($this);
780
781 13
            case Query::HYDRATE_SIMPLEOBJECT:
782
                return new Internal\Hydration\SimpleObjectHydrator($this);
783
784 407
            default:
785
                $class = $this->config->getCustomHydrationMode((string) $hydrationMode);
786
                if ($class !== null) {
787 1
                    return new $class($this);
788 1
                }
789 1
        }
790
791
        throw ORMException::invalidHydrationMode($hydrationMode);
792
    }
793
794
    /**
795
     * {@inheritDoc}
796
     */
797
    public function getProxyFactory()
798
    {
799 166
        return $this->proxyFactory;
800
    }
801 166
802
    /**
803
     * {@inheritDoc}
804
     */
805
    public function initializeObject($obj)
806
    {
807
        $this->unitOfWork->initializeObject($obj);
808
    }
809
810
    /**
811
     * Factory method to create EntityManager instances.
812
     *
813
     * @param Connection|mixed[] $connection   An array with the connection parameters or an existing Connection instance.
814
     * @param Configuration      $config       The Configuration instance to use.
815
     * @param EventManager       $eventManager The EventManager instance to use.
816
     *
817
     * @return EntityManager The created EntityManager.
818
     *
819
     * @throws \InvalidArgumentException
820
     * @throws ORMException
821
     */
822
    public static function create($connection, Configuration $config, ?EventManager $eventManager = null)
823
    {
824 2247
        if (! $config->getMetadataDriverImpl()) {
825
            throw ORMException::missingMappingDriverImpl();
826 2247
        }
827
828
        $connection = static::createConnection($connection, $config, $eventManager);
829
830 2247
        $metadataFactoryClassName = $config->getClassMetadataFactoryName();
831
        /** @var \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory $metadataFactory */
832 2247
        $metadataFactory          = new $metadataFactoryClassName($config, $connection, $eventManager);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 10 spaces
Loading history...
833
834
        return self::createWithClassMetadataFactory($connection, $config, $eventManager, $metadataFactory);
0 ignored issues
show
Bug introduced by
It seems like $eventManager can also be of type null; however, parameter $eventManager of Doctrine\ORM\EntityManag...hClassMetadataFactory() does only seem to accept Doctrine\Common\EventManager, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

834
        return self::createWithClassMetadataFactory($connection, $config, /** @scrutinizer ignore-type */ $eventManager, $metadataFactory);
Loading history...
835
    }
836
837
    public static function createWithClassMetadataFactory(Connection $connection, Configuration $config, EventManager $eventManager, ClassMetadataFactory $metadataFactory)
838
    {
839
        $metadatas = MetadataCollection::fromClassMetadatas($metadataFactory->getAllMetadata());
840
841
        return new self($connection, $config, $eventManager, $metadatas);
842
    }
843
844
    public static function createWithClassMetadata(Connection $conn, Configuration $config, EventManager $eventManager, MetadataCollection $metadatas)
845
    {
846
        return new self($conn, $config, $eventManager, $metadatas);
847 2247
    }
848
849 2247
    /**
850
     * Factory method to create Connection instances.
851
     *
852
     * @param Connection|mixed[] $connection   An array with the connection parameters or an existing Connection instance.
853 2247
     * @param Configuration      $config       The Configuration instance to use.
854 1
     * @param EventManager       $eventManager The EventManager instance to use.
855 1
     *
856 1
     * @return Connection
857 1
     *
858 1
     * @throws \InvalidArgumentException
859
     * @throws ORMException
860
     */
861
    protected static function createConnection($connection, Configuration $config, ?EventManager $eventManager = null)
862
    {
863 2247
        if (is_array($connection)) {
864
            return DriverManager::getConnection($connection, $config, $eventManager ?: new EventManager());
865
        }
866
867 2247
        if (! $connection instanceof Connection) {
0 ignored issues
show
introduced by
$connection is always a sub-type of Doctrine\DBAL\Connection.
Loading history...
868
            throw new \InvalidArgumentException(
869
                sprintf(
870
                    'Invalid $connection argument of type %s given%s.',
871
                    is_object($connection) ? get_class($connection) : gettype($connection),
872
                    is_object($connection) ? '' : ': "' . $connection . '"'
873 588
                )
874
            );
875 588
        }
876 588
877
        if ($eventManager !== null && $connection->getEventManager() !== $eventManager) {
878
            throw ORMException::mismatchedEventManager();
879 588
        }
880
881
        return $connection;
882
    }
883
884
    /**
885 40
     * {@inheritDoc}
886
     */
887 40
    public function getFilters()
888
    {
889
        if ($this->filterCollection === null) {
890
            $this->filterCollection = new FilterCollection($this);
891
        }
892
893 765
        return $this->filterCollection;
894
    }
895 765
896
    /**
897
     * {@inheritDoc}
898
     */
899
    public function isFiltersStateClean()
900
    {
901
        return $this->filterCollection === null || $this->filterCollection->isClean();
902
    }
903
904
    /**
905
     * {@inheritDoc}
906
     */
907
    public function hasFilters()
908
    {
909
        return $this->filterCollection !== null;
910
    }
911
}
912