Failed Conditions
Pull Request — master (#7143)
by Mike
07:18
created

EntityManager::getMetadataFactory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
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;
0 ignored issues
show
introduced by
Type Doctrine\ORM\Utility\StaticClassNameConverter is not used in this file.
Loading history...
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 sprintf;
29
30
/**
31
 * The EntityManager is the central access point to ORM functionality.
32
 *
33
 * It is a facade to all different ORM subsystems such as UnitOfWork,
34
 * Query Language and Repository API. Instantiation is done through
35
 * the static create() method. The quickest way to obtain a fully
36
 * configured EntityManager is:
37
 *
38
 *     use Doctrine\ORM\Tools\Setup;
39
 *     use Doctrine\ORM\EntityManager;
40
 *
41
 *     $paths = array('/path/to/entity/mapping/files');
42
 *
43
 *     $config = Setup::createAnnotationMetadataConfiguration($paths);
44
 *     $dbParams = array('driver' => 'pdo_sqlite', 'memory' => true);
45
 *     $entityManager = EntityManager::create($dbParams, $config);
46
 *
47
 * For more information see
48
 * {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html}
49
 *
50
 * You should never attempt to inherit from the EntityManager: Inheritance
51
 * is not a valid extension point for the EntityManager. Instead you
52
 * should take a look at the {@see \Doctrine\ORM\Decorator\EntityManagerDecorator}
53
 * and wrap your entity manager in a decorator.
54
 */
55
final class EntityManager implements EntityManagerInterface
56
{
57
    /**
58
     * The used Configuration.
59
     *
60
     * @var Configuration
61
     */
62
    private $config;
63
64
    /**
65
     * The database connection used by the EntityManager.
66
     *
67
     * @var Connection
68
     */
69
    private $conn;
70
71
    /**
72
     * The UnitOfWork used to coordinate object-level transactions.
73
     *
74
     * @var UnitOfWork
75
     */
76
    private $unitOfWork;
77
78
    /**
79
     * The event manager that is the central point of the event system.
80
     *
81
     * @var EventManager
82
     */
83
    private $eventManager;
84
85
    /**
86
     * The proxy factory used to create dynamic proxies.
87
     *
88
     * @var ProxyFactory
89
     */
90
    private $proxyFactory;
91
92
    /**
93
     * The repository factory used to create dynamic repositories.
94
     *
95
     * @var RepositoryFactory
96
     */
97
    private $repositoryFactory;
98
99
    /**
100
     * The expression builder instance used to generate query expressions.
101
     *
102
     * @var Expr
103
     */
104
    private $expressionBuilder;
105
106
    /**
107
     * The IdentifierFlattener used for manipulating identifiers
108
     *
109
     * @var IdentifierFlattener
110
     */
111
    private $identifierFlattener;
112
113
    /**
114
     * Whether the EntityManager is closed or not.
115
     *
116
     * @var bool
117
     */
118
    private $closed = false;
119
120
    /**
121
     * Collection of query filters.
122
     *
123
     * @var FilterCollection
124
     */
125
    private $filterCollection;
126
127
    /** @var Cache The second level cache regions API. */
128
    private $cache;
129
130
    /** @var MetadataCollection */
131
    private $mappings;
132
133
    /**
134
     * Creates a new EntityManager that operates on the given database connection
135
     * and uses the given Configuration and EventManager implementations.
136
     *
137
     */
138
    protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager, MetadataCollection $metadataCollection)
139
    {
140
        $this->conn                = $conn;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 9 spaces but found 16 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...
141
        $this->config              = $config;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 7 spaces but found 14 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->eventManager        = $eventManager;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 1 space but found 8 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->mappings            = $metadataCollection;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 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...
144 2247
145 2247
        $this->repositoryFactory   = $config->getRepositoryFactory();
146 2247
        $this->unitOfWork          = new UnitOfWork($this, $metadataCollection);
147
        $this->proxyFactory        = new StaticProxyFactory($this, $this->config->buildGhostObjectFactory());
148 2247
        $this->identifierFlattener = new IdentifierFlattener($this->unitOfWork, $this->mappings);
149
    }
150 2247
151
    /**
152 2247
     * {@inheritDoc}
153 2247
     */
154
    public function getConnection()
155 2247
    {
156 2247
        return $this->conn;
157 2247
    }
158 2247
159
    /**
160 2247
     * {@inheritDoc}
161 280
     */
162 280
    public function getExpressionBuilder()
163 280
    {
164
        if ($this->expressionBuilder === null) {
165 2247
            $this->expressionBuilder = new Query\Expr();
166
        }
167
168
        return $this->expressionBuilder;
169
    }
170 1781
171
    public function getIdentifierFlattener() : IdentifierFlattener
172 1781
    {
173
        return $this->identifierFlattener;
174
    }
175
176
    /**
177
     * {@inheritDoc}
178
     */
179
    public function beginTransaction()
180 812
    {
181
        $this->conn->beginTransaction();
182 812
    }
183
184
    /**
185
     * {@inheritDoc}
186
     */
187
    public function getCache()
188 17
    {
189
        return $this->cache;
190 17
    }
191 17
192
    /**
193
     * {@inheritDoc}
194 17
     */
195
    public function transactional(callable $func)
196
    {
197 1094
        $this->conn->beginTransaction();
198
199 1094
        try {
200
            $return = $func($this);
201
202
            $this->flush();
203
            $this->conn->commit();
204
205 1
            return $return;
206
        } catch (\Throwable $e) {
207 1
            $this->close();
208 1
            $this->conn->rollBack();
209
210
            throw $e;
211
        }
212
    }
213 212
214
    /**
215 212
     * {@inheritDoc}
216
     */
217
    public function commit()
218
    {
219
        $this->conn->commit();
220
    }
221 6
222
    /**
223 6
     * {@inheritDoc}
224
     */
225
    public function rollback()
226 6
    {
227
        $this->conn->rollBack();
228 5
    }
229 5
230
    /**
231 5
     * Returns the ORM metadata descriptor for a class.
232 1
     *
233 1
     * The class name must be the fully-qualified class name without a leading backslash
234 1
     * (as it is returned by get_class($obj)) or an aliased class name.
235
     *
236 1
     * Examples:
237
     * MyProject\Domain\User
238
     * sales:PriceRequest
239
     *
240
     * {@internal Performance-sensitive method. }}
241
     *
242
     * @param string $className
243 1
     *
244
     * @throws \ReflectionException
245 1
     * @throws \InvalidArgumentException
246 1
     * @throws MappingException
247
     */
248
    public function getClassMetadata($className) : Mapping\ClassMetadata
249
    {
250
        return $this->mappings->get($className);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->mappings->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...
251
    }
252
253
    /**
254
     * {@inheritDoc}
255
     */
256
    public function createQuery($dql = '')
257
    {
258
        $query = new Query($this);
259
260
        if (! empty($dql)) {
261
            $query->setDQL($dql);
262
        }
263
264
        return $query;
265
    }
266
267
    /**
268
     * {@inheritDoc}
269
     */
270
    public function createNativeQuery($sql, ResultSetMapping $rsm)
271
    {
272
        $query = new NativeQuery($this);
273
274 1894
        $query->setSQL($sql);
275
        $query->setResultSetMapping($rsm);
276 1894
277
        return $query;
278
    }
279
280
    /**
281
     * {@inheritDoc}
282 954
     */
283
    public function createQueryBuilder()
284 954
    {
285
        return new QueryBuilder($this);
286 954
    }
287 949
288
    /**
289
     * {@inheritDoc}
290 954
     *
291
     * @deprecated
292
     */
293
    public function merge($object)
294
    {
295
        throw new \BadMethodCallException('@TODO method disabled - will be removed in 3.0 with a release of doctrine/common');
296 13
    }
297
298 13
    /**
299
     * {@inheritDoc}
300 13
     *
301 13
     * @deprecated
302
     */
303 13
    public function detach($object)
304
    {
305
        throw new \BadMethodCallException('@TODO method disabled - will be removed in 3.0 with a release of doctrine/common');
306
    }
307
308
    /**
309 128
     * Flushes all changes to objects that have been queued up to now to the database.
310
     * This effectively synchronizes the in-memory state of managed objects with the
311 128
     * database.
312
     *
313
     * If an entity is explicitly passed to this method only this entity and
314
     * the cascade-persist semantics + scheduled inserts/removals are synchronized.
315
     *
316
     * @throws OptimisticLockException If a version check on an entity that
317
     *         makes use of optimistic locking fails.
318
     * @throws ORMException
319
     */
320
    public function flush()
321
    {
322
        $this->errorIfClosed();
323
324
        $this->unitOfWork->commit();
325
    }
326
327
    /**
328
     * Finds an Entity by its identifier.
329
     *
330
     * @param string   $entityName  The class name of the entity to find.
331
     * @param mixed    $id          The identity of the entity to find.
332
     * @param int|null $lockMode    One of the \Doctrine\DBAL\LockMode::* constants
333
     *                              or NULL if no specific lock mode should be used
334
     *                              during the search.
335
     * @param int|null $lockVersion The version of the entity to find when using
336
     *                              optimistic locking.
337
     *
338
     * @return object|null The entity instance or NULL if the entity can not be found.
339
     *
340
     * @throws OptimisticLockException
341
     * @throws ORMInvalidArgumentException
342
     * @throws TransactionRequiredException
343
     * @throws ORMException
344
     */
345
    public function find($entityName, $id, $lockMode = null, $lockVersion = null)
346 1005
    {
347
        $class     = $this->mappings->get($entityName);
348 1005
        $className = $class->getClassName();
349
350 1004
        if (! is_array($id)) {
351 994
            if ($class->isIdentifierComposite()) {
352
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
353
            }
354
355
            $id = [$class->identifier[0] => $id];
356
        }
357
358
        foreach ($id as $i => $value) {
359
            if (is_object($value)) {
360
                try{
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after TRY keyword; 0 found
Loading history...
361
                    $this->mappings->get($value);
0 ignored issues
show
Bug introduced by
$value of type object is incompatible with the type string expected by parameter $name of Doctrine\ORM\Mapping\MetadataCollection::get(). ( Ignorable by Annotation )

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

361
                    $this->mappings->get(/** @scrutinizer ignore-type */ $value);
Loading history...
362
                    $id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
363
                } catch (\Exception $e) {
364
                    $id[$i] = null;
365
                }
366
367
                if ($id[$i] === null) {
368
                    throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
369
                }
370
            }
371 403
        }
372
373 403
        $sortedId = [];
374 403
375
        foreach ($class->identifier as $identifier) {
376 403
            if (! isset($id[$identifier])) {
377 371
                throw ORMException::missingIdentifierField($className, $identifier);
378
            }
379
380
            $sortedId[$identifier] = $id[$identifier];
381 371
            unset($id[$identifier]);
382
        }
383
384 403
        if ($id) {
385 403
            throw ORMException::unrecognizedIdentifierFields($className, array_keys($id));
386 10
        }
387
388 10
        $unitOfWork = $this->getUnitOfWork();
389 403
390
        // Check identity map first
391
        $entity = $unitOfWork->tryGetById($sortedId, $class->getRootClassName());
392
        if ($entity !== false) {
393
            if (! ($entity instanceof $className)) {
394 402
                return null;
395
            }
396 402
397 402
            switch (true) {
398 1
                case $lockMode === LockMode::OPTIMISTIC:
399
                    $this->lock($entity, $lockMode, $lockVersion);
400
                    break;
401 401
402 401
                case $lockMode === LockMode::NONE:
403
                case $lockMode === LockMode::PESSIMISTIC_READ:
404
                case $lockMode === LockMode::PESSIMISTIC_WRITE:
405 401
                    $persister = $unitOfWork->getEntityPersister($className);
406 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

406
                    $persister->refresh($sortedId, /** @scrutinizer ignore-type */ $entity, $lockMode);
Loading history...
407
                    break;
408
            }
409 400
410
            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...
411
        }
412 400
413 400
        $persister = $unitOfWork->getEntityPersister($className);
414 45
415 1
        switch (true) {
416
            case $lockMode === LockMode::OPTIMISTIC:
417
                if (! $class->isVersioned()) {
418
                    throw OptimisticLockException::notVersioned($className);
419 44
                }
420 1
421
                $entity = $persister->load($sortedId);
422
423 44
                $unitOfWork->lock($entity, $lockMode, $lockVersion);
424 44
425 44
                return $entity;
426
427
            case $lockMode === LockMode::PESSIMISTIC_READ:
428
            case $lockMode === LockMode::PESSIMISTIC_WRITE:
429
                if (! $this->getConnection()->isTransactionActive()) {
430
                    throw TransactionRequiredException::transactionRequired();
431 44
                }
432
433
                return $persister->load($sortedId, null, null, [], $lockMode);
434 379
435
            default:
436
                return $persister->loadById($sortedId);
437 379
        }
438 1
    }
439 1
440
    /**
441
     * {@inheritDoc}
442
     */
443
    public function getReference($entityName, $id)
444
    {
445
        $class     = $this->mappings->get($entityName);
446
        $className = $class->getClassName();
447
448 378
        if (! is_array($id)) {
449 377
            if ($class->isIdentifierComposite()) {
450 2
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
451 2
            }
452
453
            $id = [$class->identifier[0] => $id];
454
        }
455
456
        $scalarId = [];
457 376
458
        foreach ($id as $i => $value) {
459
            $scalarId[$i] = $value;
460
461
            if (is_object($value)) {
462
                try{
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after TRY keyword; 0 found
Loading history...
463
                    $this->mappings->get($value);
0 ignored issues
show
Bug introduced by
$value of type object is incompatible with the type string expected by parameter $name of Doctrine\ORM\Mapping\MetadataCollection::get(). ( Ignorable by Annotation )

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

463
                    $this->mappings->get(/** @scrutinizer ignore-type */ $value);
Loading history...
464 90
                    $scalarId[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
465
                } catch (\Exception $e) {
466 90
                    $scalarId[$i] = null;
467 90
                }
468
469 90
                if ($scalarId[$i] === null) {
470 39
                    throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
471
                }
472
            }
473
        }
474 39
475
        $sortedId = [];
476
477 90
        foreach ($class->identifier as $identifier) {
478
            if (! isset($scalarId[$identifier])) {
479 90
                throw ORMException::missingIdentifierField($className, $identifier);
480 90
            }
481
482 90
            $sortedId[$identifier] = $scalarId[$identifier];
483 2
            unset($scalarId[$identifier]);
484
        }
485 2
486 90
        if ($scalarId) {
487
            throw ORMException::unrecognizedIdentifierFields($className, array_keys($scalarId));
488
        }
489
490
        // Check identity map first, if its already in there just return it.
491 90
        $entity = $this->unitOfWork->tryGetById($sortedId, $class->getRootClassName());
492
        if ($entity !== false) {
493 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...
494 90
        }
495
496
        if ($class->getSubClasses()) {
497
            return $this->find($entityName, $sortedId);
498 90
        }
499 90
500
        $entity = $this->proxyFactory->getProxy($class, $id);
501
502 90
        $this->unitOfWork->registerManaged($entity, $sortedId, []);
503 1
504
        if ($entity instanceof EntityManagerAware) {
505
            $entity->injectEntityManager($this, $class);
506
        }
507 89
508 89
        return $entity;
509 29
    }
510
511
    /**
512 84
     * {@inheritDoc}
513 2
     */
514
    public function getPartialReference($entityName, $id)
515
    {
516 84
        $class     = $this->mappings->get($entityName);
517
        $className = $class->getClassName();
518 84
519
        if (! is_array($id)) {
520 84
            if ($class->isIdentifierComposite()) {
521 3
                throw ORMInvalidArgumentException::invalidCompositeIdentifier();
522
            }
523
524 84
            $id = [$class->identifier[0] => $id];
525
        }
526
527
        foreach ($id as $i => $value) {
528
            if (is_object($value)) {
529
                try{
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after TRY keyword; 0 found
Loading history...
530 4
                    $this->mappings->get($value);
0 ignored issues
show
Bug introduced by
$value of type object is incompatible with the type string expected by parameter $name of Doctrine\ORM\Mapping\MetadataCollection::get(). ( Ignorable by Annotation )

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

530
                    $this->mappings->get(/** @scrutinizer ignore-type */ $value);
Loading history...
531
                    $id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
532 4
                } catch (\Exception $e) {
533 4
                    $id[$i] = null;
534
                }
535 4
536 4
                if ($id[$i] === null) {
537
                    throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
538
                }
539
            }
540 4
        }
541
542
        $sortedId = [];
543 4
544 4
        foreach ($class->identifier as $identifier) {
545
            if (! isset($id[$identifier])) {
546
                throw ORMException::missingIdentifierField($className, $identifier);
547
            }
548 4
549
            $sortedId[$identifier] = $id[$identifier];
550
            unset($id[$identifier]);
551
        }
552
553 4
        if ($id) {
554
            throw ORMException::unrecognizedIdentifierFields($className, array_keys($id));
555 4
        }
556 4
557
        // Check identity map first, if its already in there just return it.
558
        $entity = $this->unitOfWork->tryGetById($sortedId, $class->getRootClassName());
559
        if ($entity !== false) {
560 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...
561 4
        }
562
563
        $persister = $this->unitOfWork->getEntityPersister($class->getClassName());
564 4
        $entity    = $this->unitOfWork->newInstance($class);
565
566
        $persister->setIdentifier($entity, $sortedId);
567
568
        $this->unitOfWork->registerManaged($entity, $sortedId, []);
569 4
        $this->unitOfWork->markReadOnly($entity);
570 4
571 1
        return $entity;
572
    }
573
574 3
    /**
575 3
     * Clears the EntityManager. All entities that are currently managed
576
     * by this EntityManager become detached.
577 3
     *
578
     * @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...
579 3
     *
580 3
     */
581
    public function clear($entityName = null)
582 3
    {
583
        $this->unitOfWork->clear();
584
585
        $this->unitOfWork = new UnitOfWork($this, $this->mappings);
586
587
        if ($this->eventManager->hasListeners(Events::onClear)) {
588
            $this->eventManager->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this));
589
        }
590
    }
591
592 1197
    /**
593
     * {@inheritDoc}
594 1197
     */
595
    public function close()
596 1197
    {
597
        $this->clear();
598 1197
599 9
        $this->closed = true;
600
    }
601 1197
602
    /**
603
     * Tells the EntityManager to make an instance managed and persistent.
604
     *
605
     * The entity will be entered into the database at or before transaction
606 18
     * commit or as a result of the flush operation.
607
     *
608 18
     * NOTE: The persist operation always considers entities that are not yet known to
609
     * this EntityManager as NEW. Do not pass detached entities to the persist operation.
610 18
     *
611 18
     * @param object $entity The instance to make managed and persistent.
612
     *
613
     * @throws ORMInvalidArgumentException
614
     * @throws ORMException
615
     */
616
    public function persist($entity)
617
    {
618
        if (! is_object($entity)) {
619
            throw ORMInvalidArgumentException::invalidObject('EntityManager#persist()', $entity);
620
        }
621
622
        $this->errorIfClosed();
623
624
        $this->unitOfWork->persist($entity);
625
    }
626
627 999
    /**
628
     * Removes an entity instance.
629 999
     *
630 1
     * A removed entity will be removed from the database at or before transaction commit
631
     * or as a result of the flush operation.
632
     *
633 998
     * @param object $entity The entity instance to remove.
634
     *
635 997
     * @throws ORMInvalidArgumentException
636 996
     * @throws ORMException
637
     */
638
    public function remove($entity)
639
    {
640
        if (! is_object($entity)) {
641
            throw ORMInvalidArgumentException::invalidObject('EntityManager#remove()', $entity);
642
        }
643
644
        $this->errorIfClosed();
645
646
        $this->unitOfWork->remove($entity);
647
    }
648
649 49
    /**
650
     * Refreshes the persistent state of an entity from the database,
651 49
     * overriding any local changes that have not yet been persisted.
652 1
     *
653
     * @param object $entity The entity to refresh.
654
     *
655 48
     * @throws ORMInvalidArgumentException
656
     * @throws ORMException
657 47
     */
658 47
    public function refresh($entity)
659
    {
660
        if (! is_object($entity)) {
661
            throw ORMInvalidArgumentException::invalidObject('EntityManager#refresh()', $entity);
662
        }
663
664
        $this->errorIfClosed();
665
666
        $this->unitOfWork->refresh($entity);
667
    }
668
669 17
    /**
670
     * {@inheritDoc}
671 17
     */
672 1
    public function lock($entity, $lockMode, $lockVersion = null)
673
    {
674
        $this->unitOfWork->lock($entity, $lockMode, $lockVersion);
675 16
    }
676
677 15
    /**
678 15
     * Gets the repository for an entity class.
679
     *
680
     * @param string $entityName The name of the entity.
681
     *
682
     * @return ObjectRepository|EntityRepository The repository class.
683 9
     */
684
    public function getRepository($entityName)
685 9
    {
686 2
        return $this->repositoryFactory->getRepository($this, $entityName);
687
    }
688
689
    /**
690
     * Determines whether an entity instance is managed in this EntityManager.
691
     *
692
     * @param object $entity
693
     *
694
     * @return bool TRUE if this EntityManager currently manages the given entity, FALSE otherwise.
695 137
     */
696
    public function contains($entity)
697 137
    {
698
        return $this->unitOfWork->isScheduledForInsert($entity)
699
            || ($this->unitOfWork->isInIdentityMap($entity) && ! $this->unitOfWork->isScheduledForDelete($entity));
700
    }
701
702
    /**
703
     * {@inheritDoc}
704
     */
705
    public function getEventManager()
706
    {
707 15
        return $this->eventManager;
708
    }
709 15
710 15
    /**
711
     * {@inheritDoc}
712
     */
713
    public function getConfiguration()
714
    {
715
        return $this->config;
716 2247
    }
717
718 2247
    /**
719
     * Throws an exception if the EntityManager is closed or currently not active.
720
     *
721
     * @throws ORMException If the EntityManager is closed.
722
     */
723
    private function errorIfClosed()
724 2247
    {
725
        if ($this->closed) {
726 2247
            throw ORMException::entityManagerClosed();
727
        }
728
    }
729
730
    /**
731
     * {@inheritDoc}
732
     */
733
    public function isOpen()
734 1010
    {
735
        return ! $this->closed;
736 1010
    }
737 4
738
    /**
739 1006
     * {@inheritDoc}
740
     */
741
    public function getUnitOfWork()
742
    {
743
        return $this->unitOfWork;
744 2
    }
745
746 2
    /**
747
     * {@inheritDoc}
748
     */
749
    public function getHydrator($hydrationMode)
750
    {
751
        return $this->newHydrator($hydrationMode);
752 1350
    }
753
754 1350
    /**
755
     * {@inheritDoc}
756
     */
757
    public function newHydrator($hydrationMode)
758
    {
759
        switch ($hydrationMode) {
760
            case Query::HYDRATE_OBJECT:
761
                return new Internal\Hydration\ObjectHydrator($this);
762
763
            case Query::HYDRATE_ARRAY:
764
                return new Internal\Hydration\ArrayHydrator($this);
765
766
            case Query::HYDRATE_SCALAR:
767
                return new Internal\Hydration\ScalarHydrator($this);
768 883
769
            case Query::HYDRATE_SINGLE_SCALAR:
770 883
                return new Internal\Hydration\SingleScalarHydrator($this);
771
772 614
            case Query::HYDRATE_SIMPLEOBJECT:
773
                return new Internal\Hydration\SimpleObjectHydrator($this);
774
775 45
            default:
776
                $class = $this->config->getCustomHydrationMode((string) $hydrationMode);
777
                if ($class !== null) {
778 85
                    return new $class($this);
779
                }
780
        }
781 13
782
        throw ORMException::invalidHydrationMode($hydrationMode);
783
    }
784 407
785
    /**
786
     * {@inheritDoc}
787 1
     */
788 1
    public function getProxyFactory()
789 1
    {
790
        return $this->proxyFactory;
791
    }
792
793
    /**
794
     * {@inheritDoc}
795
     */
796
    public function initializeObject($obj)
797
    {
798
        $this->unitOfWork->initializeObject($obj);
799 166
    }
800
801 166
    /**
802
     * Factory method to create EntityManager instances.
803
     *
804
     * @param Connection|mixed[] $connection   An array with the connection parameters or an existing Connection instance.
805
     * @param Configuration      $config       The Configuration instance to use.
806
     * @param EventManager       $eventManager The EventManager instance to use.
807
     *
808
     * @return EntityManager The created EntityManager.
809
     *
810
     * @throws \InvalidArgumentException
811
     * @throws ORMException
812
     */
813
    public static function create($connection, Configuration $config, EventManager $eventManager)
814
    {
815
        if (! $config->getMetadataDriverImpl()) {
816
            throw ORMException::missingMappingDriverImpl();
817
        }
818
819
        $connection = static::createConnection($connection, $config, $eventManager);
820
821
        $metadataFactoryClassName = $config->getClassMetadataFactoryName();
822
        /** @var \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory $metadataFactory */
823
        $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...
824 2247
825
        return self::createWithClassMetadataFactory($connection, $config, $eventManager, $metadataFactory);
826 2247
    }
827
828
    public static function createWithClassMetadataFactory(Connection $connection, Configuration $config, EventManager $eventManager, ClassMetadataFactory $metadataFactory)
829
    {
830 2247
        $metadatas = MetadataCollection::fromClassMetadatas(...$metadataFactory->getAllMetadata());
0 ignored issues
show
Bug introduced by
$metadataFactory->getAllMetadata() is expanded, but the parameter $firstClass of Doctrine\ORM\Mapping\Met...n::fromClassMetadatas() does not expect variable arguments. ( Ignorable by Annotation )

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

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