Failed Conditions
Push — master ( ddccd4...8ad3df )
by Marco
13:34
created

assertThatThereAreNoUnintentionallyNonPersistedAssociations()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 7
cts 7
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 0
crap 2
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\ORM;
21
22
use Doctrine\Common\Collections\ArrayCollection;
23
use Doctrine\Common\Collections\Collection;
24
use Doctrine\Common\NotifyPropertyChanged;
25
use Doctrine\Common\Persistence\Mapping\RuntimeReflectionService;
26
use Doctrine\Common\Persistence\ObjectManagerAware;
27
use Doctrine\Common\PropertyChangedListener;
28
use Doctrine\DBAL\LockMode;
29
use Doctrine\ORM\Cache\Persister\CachedPersister;
30
use Doctrine\ORM\Event\LifecycleEventArgs;
31
use Doctrine\ORM\Event\ListenersInvoker;
32
use Doctrine\ORM\Event\OnFlushEventArgs;
33
use Doctrine\ORM\Event\PostFlushEventArgs;
34
use Doctrine\ORM\Event\PreFlushEventArgs;
35
use Doctrine\ORM\Event\PreUpdateEventArgs;
36
use Doctrine\ORM\Internal\HydrationCompleteHandler;
37
use Doctrine\ORM\Mapping\ClassMetadata;
38
use Doctrine\ORM\Mapping\Reflection\ReflectionPropertiesGetter;
39
use Doctrine\ORM\Persisters\Collection\ManyToManyPersister;
40
use Doctrine\ORM\Persisters\Collection\OneToManyPersister;
41
use Doctrine\ORM\Persisters\Entity\BasicEntityPersister;
42
use Doctrine\ORM\Persisters\Entity\JoinedSubclassPersister;
43
use Doctrine\ORM\Persisters\Entity\SingleTablePersister;
44
use Doctrine\ORM\Proxy\Proxy;
45
use Doctrine\ORM\Utility\IdentifierFlattener;
46
use Exception;
47
use InvalidArgumentException;
48
use UnexpectedValueException;
49
50
/**
51
 * The UnitOfWork is responsible for tracking changes to objects during an
52
 * "object-level" transaction and for writing out changes to the database
53
 * in the correct order.
54
 *
55
 * Internal note: This class contains highly performance-sensitive code.
56
 *
57
 * @since       2.0
58
 * @author      Benjamin Eberlei <[email protected]>
59
 * @author      Guilherme Blanco <[email protected]>
60
 * @author      Jonathan Wage <[email protected]>
61
 * @author      Roman Borschel <[email protected]>
62
 * @author      Rob Caiger <[email protected]>
63
 */
64
class UnitOfWork implements PropertyChangedListener
65
{
66
    /**
67
     * An entity is in MANAGED state when its persistence is managed by an EntityManager.
68
     */
69
    const STATE_MANAGED = 1;
70
71
    /**
72
     * An entity is new if it has just been instantiated (i.e. using the "new" operator)
73
     * and is not (yet) managed by an EntityManager.
74
     */
75
    const STATE_NEW = 2;
76
77
    /**
78
     * A detached entity is an instance with persistent state and identity that is not
79
     * (or no longer) associated with an EntityManager (and a UnitOfWork).
80
     */
81
    const STATE_DETACHED = 3;
82
83
    /**
84
     * A removed entity instance is an instance with a persistent identity,
85
     * associated with an EntityManager, whose persistent state will be deleted
86
     * on commit.
87
     */
88
    const STATE_REMOVED = 4;
89
90
    /**
91
     * Hint used to collect all primary keys of associated entities during hydration
92
     * and execute it in a dedicated query afterwards
93
     * @see https://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html?highlight=eager#temporarily-change-fetch-mode-in-dql
94
     */
95
    const HINT_DEFEREAGERLOAD = 'deferEagerLoad';
96
97
    /**
98
     * The identity map that holds references to all managed entities that have
99
     * an identity. The entities are grouped by their class name.
100
     * Since all classes in a hierarchy must share the same identifier set,
101
     * we always take the root class name of the hierarchy.
102
     *
103
     * @var array
104
     */
105
    private $identityMap = [];
106
107
    /**
108
     * Map of all identifiers of managed entities.
109
     * Keys are object ids (spl_object_hash).
110
     *
111
     * @var array
112
     */
113
    private $entityIdentifiers = [];
114
115
    /**
116
     * Map of the original entity data of managed entities.
117
     * Keys are object ids (spl_object_hash). This is used for calculating changesets
118
     * at commit time.
119
     *
120
     * Internal note: Note that PHPs "copy-on-write" behavior helps a lot with memory usage.
121
     *                A value will only really be copied if the value in the entity is modified
122
     *                by the user.
123
     *
124
     * @var array
125
     */
126
    private $originalEntityData = [];
127
128
    /**
129
     * Map of entity changes. Keys are object ids (spl_object_hash).
130
     * Filled at the beginning of a commit of the UnitOfWork and cleaned at the end.
131
     *
132
     * @var array
133
     */
134
    private $entityChangeSets = [];
135
136
    /**
137
     * The (cached) states of any known entities.
138
     * Keys are object ids (spl_object_hash).
139
     *
140
     * @var array
141
     */
142
    private $entityStates = [];
143
144
    /**
145
     * Map of entities that are scheduled for dirty checking at commit time.
146
     * This is only used for entities with a change tracking policy of DEFERRED_EXPLICIT.
147
     * Keys are object ids (spl_object_hash).
148
     *
149
     * @var array
150
     */
151
    private $scheduledForSynchronization = [];
152
153
    /**
154
     * A list of all pending entity insertions.
155
     *
156
     * @var array
157
     */
158
    private $entityInsertions = [];
159
160
    /**
161
     * A list of all pending entity updates.
162
     *
163
     * @var array
164
     */
165
    private $entityUpdates = [];
166
167
    /**
168
     * Any pending extra updates that have been scheduled by persisters.
169
     *
170
     * @var array
171
     */
172
    private $extraUpdates = [];
173
174
    /**
175
     * A list of all pending entity deletions.
176
     *
177
     * @var array
178
     */
179
    private $entityDeletions = [];
180
181
    /**
182
     * New entities that were discovered through relationships that were not
183
     * marked as cascade-persist. During flush, this array is populated and
184
     * then pruned of any entities that were discovered through a valid
185
     * cascade-persist path. (Leftovers cause an error.)
186
     *
187
     * Keys are OIDs, payload is a two-item array describing the association
188
     * and the entity.
189
     *
190
     * @var object[][]|array[][] indexed by respective object spl_object_hash()
191
     */
192
    private $nonCascadedNewDetectedEntities = [];
193
194
    /**
195
     * All pending collection deletions.
196
     *
197
     * @var array
198
     */
199
    private $collectionDeletions = [];
200
201
    /**
202
     * All pending collection updates.
203
     *
204
     * @var array
205
     */
206
    private $collectionUpdates = [];
207
208
    /**
209
     * List of collections visited during changeset calculation on a commit-phase of a UnitOfWork.
210
     * At the end of the UnitOfWork all these collections will make new snapshots
211
     * of their data.
212
     *
213
     * @var array
214
     */
215
    private $visitedCollections = [];
216
217
    /**
218
     * The EntityManager that "owns" this UnitOfWork instance.
219
     *
220
     * @var EntityManagerInterface
221
     */
222
    private $em;
223
224
    /**
225
     * The entity persister instances used to persist entity instances.
226
     *
227
     * @var array
228
     */
229
    private $persisters = [];
230
231
    /**
232
     * The collection persister instances used to persist collections.
233
     *
234
     * @var array
235
     */
236
    private $collectionPersisters = [];
237
238
    /**
239
     * The EventManager used for dispatching events.
240
     *
241
     * @var \Doctrine\Common\EventManager
242
     */
243
    private $evm;
244
245
    /**
246
     * The ListenersInvoker used for dispatching events.
247
     *
248
     * @var \Doctrine\ORM\Event\ListenersInvoker
249
     */
250
    private $listenersInvoker;
251
252
    /**
253
     * The IdentifierFlattener used for manipulating identifiers
254
     *
255
     * @var \Doctrine\ORM\Utility\IdentifierFlattener
256
     */
257
    private $identifierFlattener;
258
259
    /**
260
     * Orphaned entities that are scheduled for removal.
261
     *
262
     * @var array
263
     */
264
    private $orphanRemovals = [];
265
266
    /**
267
     * Read-Only objects are never evaluated
268
     *
269
     * @var array
270
     */
271
    private $readOnlyObjects = [];
272
273
    /**
274
     * Map of Entity Class-Names and corresponding IDs that should eager loaded when requested.
275
     *
276
     * @var array
277
     */
278
    private $eagerLoadingEntities = [];
279
280
    /**
281
     * @var boolean
282
     */
283
    protected $hasCache = false;
284
285
    /**
286
     * Helper for handling completion of hydration
287
     *
288
     * @var HydrationCompleteHandler
289
     */
290
    private $hydrationCompleteHandler;
291
292
    /**
293
     * @var ReflectionPropertiesGetter
294
     */
295
    private $reflectionPropertiesGetter;
296
297
    /**
298
     * Initializes a new UnitOfWork instance, bound to the given EntityManager.
299
     *
300
     * @param EntityManagerInterface $em
301
     */
302 2411
    public function __construct(EntityManagerInterface $em)
303
    {
304 2411
        $this->em                         = $em;
305 2411
        $this->evm                        = $em->getEventManager();
306 2411
        $this->listenersInvoker           = new ListenersInvoker($em);
307 2411
        $this->hasCache                   = $em->getConfiguration()->isSecondLevelCacheEnabled();
308 2411
        $this->identifierFlattener        = new IdentifierFlattener($this, $em->getMetadataFactory());
309 2411
        $this->hydrationCompleteHandler   = new HydrationCompleteHandler($this->listenersInvoker, $em);
310 2411
        $this->reflectionPropertiesGetter = new ReflectionPropertiesGetter(new RuntimeReflectionService());
311 2411
    }
312
313
    /**
314
     * Commits the UnitOfWork, executing all operations that have been postponed
315
     * up to this point. The state of all managed entities will be synchronized with
316
     * the database.
317
     *
318
     * The operations are executed in the following order:
319
     *
320
     * 1) All entity insertions
321
     * 2) All entity updates
322
     * 3) All collection deletions
323
     * 4) All collection updates
324
     * 5) All entity deletions
325
     *
326
     * @param null|object|array $entity
327
     *
328
     * @return void
329
     *
330
     * @throws \Exception
331
     */
332 1050
    public function commit($entity = null)
333
    {
334
        // Raise preFlush
335 1050
        if ($this->evm->hasListeners(Events::preFlush)) {
336 2
            $this->evm->dispatchEvent(Events::preFlush, new PreFlushEventArgs($this->em));
337
        }
338
339
        // Compute changes done since last commit.
340 1050
        if (null === $entity) {
341 1040
            $this->computeChangeSets();
342 18
        } elseif (is_object($entity)) {
343 16
            $this->computeSingleEntityChangeSet($entity);
344 2
        } elseif (is_array($entity)) {
345 2
            foreach ($entity as $object) {
346 2
                $this->computeSingleEntityChangeSet($object);
347
            }
348
        }
349
350 1049
        if ( ! ($this->entityInsertions ||
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->entityInsertions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
351 170
                $this->entityDeletions ||
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->entityDeletions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
352 134
                $this->entityUpdates ||
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->entityUpdates of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
353 41
                $this->collectionUpdates ||
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->collectionUpdates of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
354 37
                $this->collectionDeletions ||
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->collectionDeletions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
355 1049
                $this->orphanRemovals)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->orphanRemovals of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
356 25
            $this->dispatchOnFlushEvent();
357 25
            $this->dispatchPostFlushEvent();
358
359 25
            return; // Nothing to do.
360
        }
361
362 1045
        $this->assertThatThereAreNoUnintentionallyNonPersistedAssociations();
363
364 1043
        if ($this->orphanRemovals) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->orphanRemovals of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
365 16
            foreach ($this->orphanRemovals as $orphan) {
366 16
                $this->remove($orphan);
367
            }
368
        }
369
370 1043
        $this->dispatchOnFlushEvent();
371
372
        // Now we need a commit order to maintain referential integrity
373 1043
        $commitOrder = $this->getCommitOrder();
374
375 1043
        $conn = $this->em->getConnection();
376 1043
        $conn->beginTransaction();
377
378
        try {
379
            // Collection deletions (deletions of complete collections)
380 1043
            foreach ($this->collectionDeletions as $collectionToDelete) {
381 19
                $this->getCollectionPersister($collectionToDelete->getMapping())->delete($collectionToDelete);
382
            }
383
384 1043
            if ($this->entityInsertions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->entityInsertions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
385 1039
                foreach ($commitOrder as $class) {
386 1039
                    $this->executeInserts($class);
387
                }
388
            }
389
390 1042
            if ($this->entityUpdates) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->entityUpdates of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
391 119
                foreach ($commitOrder as $class) {
392 119
                    $this->executeUpdates($class);
393
                }
394
            }
395
396
            // Extra updates that were requested by persisters.
397 1038
            if ($this->extraUpdates) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->extraUpdates of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
398 44
                $this->executeExtraUpdates();
399
            }
400
401
            // Collection updates (deleteRows, updateRows, insertRows)
402 1038
            foreach ($this->collectionUpdates as $collectionToUpdate) {
403 535
                $this->getCollectionPersister($collectionToUpdate->getMapping())->update($collectionToUpdate);
404
            }
405
406
            // Entity deletions come last and need to be in reverse commit order
407 1038
            if ($this->entityDeletions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->entityDeletions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
408 63
                for ($count = count($commitOrder), $i = $count - 1; $i >= 0 && $this->entityDeletions; --$i) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->entityDeletions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
409 63
                    $this->executeDeletions($commitOrder[$i]);
410
                }
411
            }
412
413 1038
            $conn->commit();
414 11
        } catch (Exception $e) {
415 11
            $this->em->close();
416 11
            $conn->rollBack();
417
418 11
            $this->afterTransactionRolledBack();
419
420 11
            throw $e;
421
        }
422
423 1038
        $this->afterTransactionComplete();
424
425
        // Take new snapshots from visited collections
426 1038
        foreach ($this->visitedCollections as $coll) {
427 534
            $coll->takeSnapshot();
428
        }
429
430 1038
        $this->dispatchPostFlushEvent();
431
432 1037
        $this->postCommitCleanup($entity);
0 ignored issues
show
Bug introduced by
It seems like $entity defined by parameter $entity on line 332 can also be of type array; however, Doctrine\ORM\UnitOfWork::postCommitCleanup() does only seem to accept null|object|array<integer,object>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and 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...
433 1037
    }
434
435
    /**
436
     * @param null|object|object[] $entity
437
     */
438 1037
    private function postCommitCleanup($entity) : void
439
    {
440 1037
        $this->entityInsertions =
441 1037
        $this->entityUpdates =
442 1037
        $this->entityDeletions =
443 1037
        $this->extraUpdates =
444 1037
        $this->collectionUpdates =
445 1037
        $this->nonCascadedNewDetectedEntities =
446 1037
        $this->collectionDeletions =
447 1037
        $this->visitedCollections =
448 1037
        $this->orphanRemovals = [];
449
450 1037
        if (null === $entity) {
451 1028
            $this->entityChangeSets = $this->scheduledForSynchronization = [];
452
453 1028
            return;
454
        }
455
456 15
        $entities = \is_object($entity)
457 13
            ? [$entity]
458 15
            : $entity;
459
460 15
        foreach ($entities as $object) {
461 15
            $oid = \spl_object_hash($object);
462
463 15
            $this->clearEntityChangeSet($oid);
464
465 15
            unset($this->scheduledForSynchronization[$this->em->getClassMetadata(\get_class($object))->rootEntityName][$oid]);
466
        }
467 15
    }
468
469
    /**
470
     * Computes the changesets of all entities scheduled for insertion.
471
     *
472
     * @return void
473
     */
474 1049
    private function computeScheduleInsertsChangeSets()
475
    {
476 1049
        foreach ($this->entityInsertions as $entity) {
477 1041
            $class = $this->em->getClassMetadata(get_class($entity));
478
479 1041
            $this->computeChangeSet($class, $entity);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
480
        }
481 1049
    }
482
483
    /**
484
     * Only flushes the given entity according to a ruleset that keeps the UoW consistent.
485
     *
486
     * 1. All entities scheduled for insertion, (orphan) removals and changes in collections are processed as well!
487
     * 2. Read Only entities are skipped.
488
     * 3. Proxies are skipped.
489
     * 4. Only if entity is properly managed.
490
     *
491
     * @param object $entity
492
     *
493
     * @return void
494
     *
495
     * @throws \InvalidArgumentException
496
     */
497 18
    private function computeSingleEntityChangeSet($entity)
498
    {
499 18
        $state = $this->getEntityState($entity);
500
501 18
        if ($state !== self::STATE_MANAGED && $state !== self::STATE_REMOVED) {
502 1
            throw new \InvalidArgumentException("Entity has to be managed or scheduled for removal for single computation " . self::objToStr($entity));
503
        }
504
505 17
        $class = $this->em->getClassMetadata(get_class($entity));
506
507 17
        if ($state === self::STATE_MANAGED && $class->isChangeTrackingDeferredImplicit()) {
508 16
            $this->persist($entity);
509
        }
510
511
        // Compute changes for INSERTed entities first. This must always happen even in this case.
512 17
        $this->computeScheduleInsertsChangeSets();
513
514 17
        if ($class->isReadOnly) {
0 ignored issues
show
Bug introduced by
Accessing isReadOnly 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...
515
            return;
516
        }
517
518
        // Ignore uninitialized proxy objects
519 17
        if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
0 ignored issues
show
Bug introduced by
Accessing __isInitialized__ on the interface Doctrine\ORM\Proxy\Proxy 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...
520 2
            return;
521
        }
522
523
        // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here.
524 15
        $oid = spl_object_hash($entity);
525
526 15 View Code Duplication
        if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) {
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...
527 6
            $this->computeChangeSet($class, $entity);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
528
        }
529 15
    }
530
531
    /**
532
     * Executes any extra updates that have been scheduled.
533
     */
534 44
    private function executeExtraUpdates()
535
    {
536 44
        foreach ($this->extraUpdates as $oid => $update) {
537 44
            list ($entity, $changeset) = $update;
538
539 44
            $this->entityChangeSets[$oid] = $changeset;
540 44
            $this->getEntityPersister(get_class($entity))->update($entity);
541
        }
542
543 44
        $this->extraUpdates = [];
544 44
    }
545
546
    /**
547
     * Gets the changeset for an entity.
548
     *
549
     * @param object $entity
550
     *
551
     * @return array
552
     */
553 1038
    public function & getEntityChangeSet($entity)
554
    {
555 1038
        $oid  = spl_object_hash($entity);
556 1038
        $data = [];
557
558 1038
        if (!isset($this->entityChangeSets[$oid])) {
559 3
            return $data;
560
        }
561
562 1038
        return $this->entityChangeSets[$oid];
563
    }
564
565
    /**
566
     * Computes the changes that happened to a single entity.
567
     *
568
     * Modifies/populates the following properties:
569
     *
570
     * {@link _originalEntityData}
571
     * If the entity is NEW or MANAGED but not yet fully persisted (only has an id)
572
     * then it was not fetched from the database and therefore we have no original
573
     * entity data yet. All of the current entity data is stored as the original entity data.
574
     *
575
     * {@link _entityChangeSets}
576
     * The changes detected on all properties of the entity are stored there.
577
     * A change is a tuple array where the first entry is the old value and the second
578
     * entry is the new value of the property. Changesets are used by persisters
579
     * to INSERT/UPDATE the persistent entity state.
580
     *
581
     * {@link _entityUpdates}
582
     * If the entity is already fully MANAGED (has been fetched from the database before)
583
     * and any changes to its properties are detected, then a reference to the entity is stored
584
     * there to mark it for an update.
585
     *
586
     * {@link _collectionDeletions}
587
     * If a PersistentCollection has been de-referenced in a fully MANAGED entity,
588
     * then this collection is marked for deletion.
589
     *
590
     * @ignore
591
     *
592
     * @internal Don't call from the outside.
593
     *
594
     * @param ClassMetadata $class  The class descriptor of the entity.
595
     * @param object        $entity The entity for which to compute the changes.
596
     *
597
     * @return void
598
     */
599 1051
    public function computeChangeSet(ClassMetadata $class, $entity)
600
    {
601 1051
        $oid = spl_object_hash($entity);
602
603 1051
        if (isset($this->readOnlyObjects[$oid])) {
604 2
            return;
605
        }
606
607 1051
        if ( ! $class->isInheritanceTypeNone()) {
608 323
            $class = $this->em->getClassMetadata(get_class($entity));
609
        }
610
611 1051
        $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preFlush) & ~ListenersInvoker::INVOKE_MANAGER;
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
612
613 1051 View Code Duplication
        if ($invoke !== ListenersInvoker::INVOKE_NONE) {
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...
614 138
            $this->listenersInvoker->invoke($class, Events::preFlush, $entity, new PreFlushEventArgs($this->em), $invoke);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
615
        }
616
617 1051
        $actualData = [];
618
619 1051
        foreach ($class->reflFields as $name => $refProp) {
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
620 1051
            $value = $refProp->getValue($entity);
621
622 1051
            if ($class->isCollectionValuedAssociation($name) && $value !== null) {
623 792
                if ($value instanceof PersistentCollection) {
624 202
                    if ($value->getOwner() === $entity) {
625 202
                        continue;
626
                    }
627
628 5
                    $value = new ArrayCollection($value->getValues());
629
                }
630
631
                // If $value is not a Collection then use an ArrayCollection.
632 787
                if ( ! $value instanceof Collection) {
633 242
                    $value = new ArrayCollection($value);
634
                }
635
636 787
                $assoc = $class->associationMappings[$name];
0 ignored issues
show
Bug introduced by
Accessing associationMappings 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...
637
638
                // Inject PersistentCollection
639 787
                $value = new PersistentCollection(
640 787
                    $this->em, $this->em->getClassMetadata($assoc['targetEntity']), $value
0 ignored issues
show
Compatibility introduced by
$this->em->getClassMetad...$assoc['targetEntity']) of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
641
                );
642 787
                $value->setOwner($entity, $assoc);
643 787
                $value->setDirty( ! $value->isEmpty());
644
645 787
                $class->reflFields[$name]->setValue($entity, $value);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
646
647 787
                $actualData[$name] = $value;
648
649 787
                continue;
650
            }
651
652 1051
            if (( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity()) && ($name !== $class->versionField)) {
0 ignored issues
show
Bug introduced by
Accessing versionField 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...
653 1051
                $actualData[$name] = $value;
654
            }
655
        }
656
657 1051
        if ( ! isset($this->originalEntityData[$oid])) {
658
            // Entity is either NEW or MANAGED but not yet fully persisted (only has an id).
659
            // These result in an INSERT.
660 1047
            $this->originalEntityData[$oid] = $actualData;
661 1047
            $changeSet = [];
662
663 1047
            foreach ($actualData as $propName => $actualValue) {
664 1025 View Code Duplication
                if ( ! isset($class->associationMappings[$propName])) {
0 ignored issues
show
Bug introduced by
Accessing associationMappings 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...
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...
665 973
                    $changeSet[$propName] = [null, $actualValue];
666
667 973
                    continue;
668
                }
669
670 915
                $assoc = $class->associationMappings[$propName];
0 ignored issues
show
Bug introduced by
Accessing associationMappings 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...
671
672 915
                if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
673 915
                    $changeSet[$propName] = [null, $actualValue];
674
                }
675
            }
676
677 1047
            $this->entityChangeSets[$oid] = $changeSet;
678
        } else {
679
            // Entity is "fully" MANAGED: it was already fully persisted before
680
            // and we have a copy of the original data
681 270
            $originalData           = $this->originalEntityData[$oid];
682 270
            $isChangeTrackingNotify = $class->isChangeTrackingNotify();
683 270
            $changeSet              = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid]))
684
                ? $this->entityChangeSets[$oid]
685 270
                : [];
686
687 270
            foreach ($actualData as $propName => $actualValue) {
688
                // skip field, its a partially omitted one!
689 255
                if ( ! (isset($originalData[$propName]) || array_key_exists($propName, $originalData))) {
690 8
                    continue;
691
                }
692
693 255
                $orgValue = $originalData[$propName];
694
695
                // skip if value haven't changed
696 255
                if ($orgValue === $actualValue) {
697 239
                    continue;
698
                }
699
700
                // if regular field
701 115 View Code Duplication
                if ( ! isset($class->associationMappings[$propName])) {
0 ignored issues
show
Bug introduced by
Accessing associationMappings 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...
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...
702 60
                    if ($isChangeTrackingNotify) {
703
                        continue;
704
                    }
705
706 60
                    $changeSet[$propName] = [$orgValue, $actualValue];
707
708 60
                    continue;
709
                }
710
711 59
                $assoc = $class->associationMappings[$propName];
0 ignored issues
show
Bug introduced by
Accessing associationMappings 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...
712
713
                // Persistent collection was exchanged with the "originally"
714
                // created one. This can only mean it was cloned and replaced
715
                // on another entity.
716 59
                if ($actualValue instanceof PersistentCollection) {
717 8
                    $owner = $actualValue->getOwner();
718 8
                    if ($owner === null) { // cloned
719
                        $actualValue->setOwner($entity, $assoc);
720 8
                    } else if ($owner !== $entity) { // no clone, we have to fix
721
                        if (!$actualValue->isInitialized()) {
722
                            $actualValue->initialize(); // we have to do this otherwise the cols share state
723
                        }
724
                        $newValue = clone $actualValue;
725
                        $newValue->setOwner($entity, $assoc);
726
                        $class->reflFields[$propName]->setValue($entity, $newValue);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
727
                    }
728
                }
729
730 59
                if ($orgValue instanceof PersistentCollection) {
731
                    // A PersistentCollection was de-referenced, so delete it.
732 8
                    $coid = spl_object_hash($orgValue);
733
734 8
                    if (isset($this->collectionDeletions[$coid])) {
735
                        continue;
736
                    }
737
738 8
                    $this->collectionDeletions[$coid] = $orgValue;
739 8
                    $changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored.
740
741 8
                    continue;
742
                }
743
744 51
                if ($assoc['type'] & ClassMetadata::TO_ONE) {
745 50
                    if ($assoc['isOwningSide']) {
746 22
                        $changeSet[$propName] = [$orgValue, $actualValue];
747
                    }
748
749 50
                    if ($orgValue !== null && $assoc['orphanRemoval']) {
750 51
                        $this->scheduleOrphanRemoval($orgValue);
751
                    }
752
                }
753
            }
754
755 270
            if ($changeSet) {
756 88
                $this->entityChangeSets[$oid]   = $changeSet;
757 88
                $this->originalEntityData[$oid] = $actualData;
758 88
                $this->entityUpdates[$oid]      = $entity;
759
            }
760
        }
761
762
        // Look for changes in associations of the entity
763 1051
        foreach ($class->associationMappings as $field => $assoc) {
0 ignored issues
show
Bug introduced by
Accessing associationMappings 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...
764 915
            if (($val = $class->reflFields[$field]->getValue($entity)) === null) {
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
765 644
                continue;
766
            }
767
768 886
            $this->computeAssociationChanges($assoc, $val);
769
770 880
            if ( ! isset($this->entityChangeSets[$oid]) &&
771 880
                $assoc['isOwningSide'] &&
772 880
                $assoc['type'] == ClassMetadata::MANY_TO_MANY &&
773 880
                $val instanceof PersistentCollection &&
774 880
                $val->isDirty()) {
775
776 35
                $this->entityChangeSets[$oid]   = [];
777 35
                $this->originalEntityData[$oid] = $actualData;
778 880
                $this->entityUpdates[$oid]      = $entity;
779
            }
780
        }
781 1045
    }
782
783
    /**
784
     * Computes all the changes that have been done to entities and collections
785
     * since the last commit and stores these changes in the _entityChangeSet map
786
     * temporarily for access by the persisters, until the UoW commit is finished.
787
     *
788
     * @return void
789
     */
790 1040
    public function computeChangeSets()
791
    {
792
        // Compute changes for INSERTed entities first. This must always happen.
793 1040
        $this->computeScheduleInsertsChangeSets();
794
795
        // Compute changes for other MANAGED entities. Change tracking policies take effect here.
796 1040
        foreach ($this->identityMap as $className => $entities) {
797 461
            $class = $this->em->getClassMetadata($className);
798
799
            // Skip class if instances are read-only
800 461
            if ($class->isReadOnly) {
0 ignored issues
show
Bug introduced by
Accessing isReadOnly 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...
801 1
                continue;
802
            }
803
804
            // If change tracking is explicit or happens through notification, then only compute
805
            // changes on entities of that type that are explicitly marked for synchronization.
806
            switch (true) {
807 460
                case ($class->isChangeTrackingDeferredImplicit()):
808 458
                    $entitiesToProcess = $entities;
809 458
                    break;
810
811 3
                case (isset($this->scheduledForSynchronization[$className])):
812 3
                    $entitiesToProcess = $this->scheduledForSynchronization[$className];
813 3
                    break;
814
815
                default:
816 1
                    $entitiesToProcess = [];
817
818
            }
819
820 460
            foreach ($entitiesToProcess as $entity) {
821
                // Ignore uninitialized proxy objects
822 440
                if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
0 ignored issues
show
Bug introduced by
Accessing __isInitialized__ on the interface Doctrine\ORM\Proxy\Proxy 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...
823 36
                    continue;
824
                }
825
826
                // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here.
827 439
                $oid = spl_object_hash($entity);
828
829 439 View Code Duplication
                if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) {
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...
830 460
                    $this->computeChangeSet($class, $entity);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
831
                }
832
            }
833
        }
834 1040
    }
835
836
    /**
837
     * Computes the changes of an association.
838
     *
839
     * @param array $assoc The association mapping.
840
     * @param mixed $value The value of the association.
841
     *
842
     * @throws ORMInvalidArgumentException
843
     * @throws ORMException
844
     *
845
     * @return void
846
     */
847 886
    private function computeAssociationChanges($assoc, $value)
848
    {
849 886
        if ($value instanceof Proxy && ! $value->__isInitialized__) {
0 ignored issues
show
Bug introduced by
Accessing __isInitialized__ on the interface Doctrine\ORM\Proxy\Proxy 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...
850 29
            return;
851
        }
852
853 885
        if ($value instanceof PersistentCollection && $value->isDirty()) {
854 537
            $coid = spl_object_hash($value);
855
856 537
            $this->collectionUpdates[$coid] = $value;
857 537
            $this->visitedCollections[$coid] = $value;
858
        }
859
860
        // Look through the entities, and in any of their associations,
861
        // for transient (new) entities, recursively. ("Persistence by reachability")
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
862
        // Unwrap. Uninitialized collections will simply be empty.
863 885
        $unwrappedValue = ($assoc['type'] & ClassMetadata::TO_ONE) ? [$value] : $value->unwrap();
864 885
        $targetClass    = $this->em->getClassMetadata($assoc['targetEntity']);
865
866 885
        foreach ($unwrappedValue as $key => $entry) {
867 737
            if (! ($entry instanceof $targetClass->name)) {
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
868 6
                throw ORMInvalidArgumentException::invalidAssociation($targetClass, $assoc, $entry);
0 ignored issues
show
Compatibility introduced by
$targetClass of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
869
            }
870
871 731
            $state = $this->getEntityState($entry, self::STATE_NEW);
872
873 731
            if ( ! ($entry instanceof $assoc['targetEntity'])) {
874
                throw ORMException::unexpectedAssociationValue($assoc['sourceEntity'], $assoc['fieldName'], get_class($entry), $assoc['targetEntity']);
875
            }
876
877
            switch ($state) {
878 731
                case self::STATE_NEW:
879 42
                    if ( ! $assoc['isCascadePersist']) {
880
                        /*
881
                         * For now just record the details, because this may
882
                         * not be an issue if we later discover another pathway
883
                         * through the object-graph where cascade-persistence
884
                         * is enabled for this object.
885
                         */
886 6
                        $this->nonCascadedNewDetectedEntities[\spl_object_hash($entry)] = [$assoc, $entry];
887
888 6
                        break;
889
                    }
890
891 37
                    $this->persistNew($targetClass, $entry);
0 ignored issues
show
Compatibility introduced by
$targetClass of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
892 37
                    $this->computeChangeSet($targetClass, $entry);
0 ignored issues
show
Compatibility introduced by
$targetClass of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
893
894 37
                    break;
895
896 723
                case self::STATE_REMOVED:
897
                    // Consume the $value as array (it's either an array or an ArrayAccess)
898
                    // and remove the element from Collection.
899 4
                    if ($assoc['type'] & ClassMetadata::TO_MANY) {
900 3
                        unset($value[$key]);
901
                    }
902 4
                    break;
903
904 723
                case self::STATE_DETACHED:
905
                    // Can actually not happen right now as we assume STATE_NEW,
906
                    // so the exception will be raised from the DBAL layer (constraint violation).
907
                    throw ORMInvalidArgumentException::detachedEntityFoundThroughRelationship($assoc, $entry);
908
                    break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
909
910 731
                default:
911
                    // MANAGED associated entities are already taken into account
912
                    // during changeset calculation anyway, since they are in the identity map.
913
            }
914
        }
915 879
    }
916
917
    /**
918
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
919
     * @param object                              $entity
920
     *
921
     * @return void
922
     */
923 1070
    private function persistNew($class, $entity)
924
    {
925 1070
        $oid    = spl_object_hash($entity);
926 1070
        $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::prePersist);
927
928 1070 View Code Duplication
        if ($invoke !== ListenersInvoker::INVOKE_NONE) {
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...
929 141
            $this->listenersInvoker->invoke($class, Events::prePersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
930
        }
931
932 1070
        $idGen = $class->idGenerator;
933
934 1070
        if ( ! $idGen->isPostInsertGenerator()) {
935 281
            $idValue = $idGen->generate($this->em, $entity);
0 ignored issues
show
Compatibility introduced by
$this->em of type object<Doctrine\ORM\EntityManagerInterface> is not a sub-type of object<Doctrine\ORM\EntityManager>. It seems like you assume a concrete implementation of the interface Doctrine\ORM\EntityManagerInterface 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...
936
937 281
            if ( ! $idGen instanceof \Doctrine\ORM\Id\AssignedGenerator) {
938 2
                $idValue = [$class->getSingleIdentifierFieldName() => $this->convertSingleFieldIdentifierToPHPValue($class, $idValue)];
939
940 2
                $class->setIdentifierValues($entity, $idValue);
941
            }
942
943 281
            $this->entityIdentifiers[$oid] = $idValue;
944
        }
945
946 1070
        $this->entityStates[$oid] = self::STATE_MANAGED;
947
948 1070
        $this->scheduleForInsert($entity);
949 1070
    }
950
951
    /**
952
     * INTERNAL:
953
     * Computes the changeset of an individual entity, independently of the
954
     * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit().
955
     *
956
     * The passed entity must be a managed entity. If the entity already has a change set
957
     * because this method is invoked during a commit cycle then the change sets are added.
958
     * whereby changes detected in this method prevail.
959
     *
960
     * @ignore
961
     *
962
     * @param ClassMetadata $class  The class descriptor of the entity.
963
     * @param object        $entity The entity for which to (re)calculate the change set.
964
     *
965
     * @return void
966
     *
967
     * @throws ORMInvalidArgumentException If the passed entity is not MANAGED.
968
     */
969 16
    public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity)
970
    {
971 16
        $oid = spl_object_hash($entity);
972
973 16
        if ( ! isset($this->entityStates[$oid]) || $this->entityStates[$oid] != self::STATE_MANAGED) {
974
            throw ORMInvalidArgumentException::entityNotManaged($entity);
975
        }
976
977
        // skip if change tracking is "NOTIFY"
978 16
        if ($class->isChangeTrackingNotify()) {
979
            return;
980
        }
981
982 16
        if ( ! $class->isInheritanceTypeNone()) {
983 3
            $class = $this->em->getClassMetadata(get_class($entity));
984
        }
985
986 16
        $actualData = [];
987
988 16
        foreach ($class->reflFields as $name => $refProp) {
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
989 16
            if (( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity())
990 16
                && ($name !== $class->versionField)
0 ignored issues
show
Bug introduced by
Accessing versionField 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...
991 16
                && ! $class->isCollectionValuedAssociation($name)) {
992 16
                $actualData[$name] = $refProp->getValue($entity);
993
            }
994
        }
995
996 16
        if ( ! isset($this->originalEntityData[$oid])) {
997
            throw new \RuntimeException('Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.');
998
        }
999
1000 16
        $originalData = $this->originalEntityData[$oid];
1001 16
        $changeSet = [];
1002
1003 16
        foreach ($actualData as $propName => $actualValue) {
1004 16
            $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
1005
1006 16
            if ($orgValue !== $actualValue) {
1007 16
                $changeSet[$propName] = [$orgValue, $actualValue];
1008
            }
1009
        }
1010
1011 16
        if ($changeSet) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $changeSet of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1012 7
            if (isset($this->entityChangeSets[$oid])) {
1013 6
                $this->entityChangeSets[$oid] = array_merge($this->entityChangeSets[$oid], $changeSet);
1014 1
            } else if ( ! isset($this->entityInsertions[$oid])) {
1015 1
                $this->entityChangeSets[$oid] = $changeSet;
1016 1
                $this->entityUpdates[$oid]    = $entity;
1017
            }
1018 7
            $this->originalEntityData[$oid] = $actualData;
1019
        }
1020 16
    }
1021
1022
    /**
1023
     * Executes all entity insertions for entities of the specified type.
1024
     *
1025
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
1026
     *
1027
     * @return void
1028
     */
1029 1039
    private function executeInserts($class)
1030
    {
1031 1039
        $entities   = [];
1032 1039
        $className  = $class->name;
1033 1039
        $persister  = $this->getEntityPersister($className);
1034 1039
        $invoke     = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist);
1035
1036 1039
        foreach ($this->entityInsertions as $oid => $entity) {
1037
1038 1039
            if ($this->em->getClassMetadata(get_class($entity))->name !== $className) {
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1039 881
                continue;
1040
            }
1041
1042 1039
            $persister->addInsert($entity);
1043
1044 1039
            unset($this->entityInsertions[$oid]);
1045
1046 1039
            if ($invoke !== ListenersInvoker::INVOKE_NONE) {
1047 1039
                $entities[] = $entity;
1048
            }
1049
        }
1050
1051 1039
        $postInsertIds = $persister->executeInserts();
1052
1053 1039
        if ($postInsertIds) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $postInsertIds of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1054
            // Persister returned post-insert IDs
1055 943
            foreach ($postInsertIds as $postInsertId) {
1056 943
                $idField = $class->getSingleIdentifierFieldName();
1057 943
                $idValue = $this->convertSingleFieldIdentifierToPHPValue($class, $postInsertId['generatedId']);
1058
1059 943
                $entity  = $postInsertId['entity'];
1060 943
                $oid     = spl_object_hash($entity);
1061
1062 943
                $class->reflFields[$idField]->setValue($entity, $idValue);
1063
1064 943
                $this->entityIdentifiers[$oid] = [$idField => $idValue];
1065 943
                $this->entityStates[$oid] = self::STATE_MANAGED;
1066 943
                $this->originalEntityData[$oid][$idField] = $idValue;
1067
1068 943
                $this->addToIdentityMap($entity);
1069
            }
1070
        }
1071
1072 1039
        foreach ($entities as $entity) {
1073 136
            $this->listenersInvoker->invoke($class, Events::postPersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
1074
        }
1075 1039
    }
1076
1077
    /**
1078
     * Executes all entity updates for entities of the specified type.
1079
     *
1080
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
1081
     *
1082
     * @return void
1083
     */
1084 119
    private function executeUpdates($class)
1085
    {
1086 119
        $className          = $class->name;
1087 119
        $persister          = $this->getEntityPersister($className);
1088 119
        $preUpdateInvoke    = $this->listenersInvoker->getSubscribedSystems($class, Events::preUpdate);
1089 119
        $postUpdateInvoke   = $this->listenersInvoker->getSubscribedSystems($class, Events::postUpdate);
1090
1091 119
        foreach ($this->entityUpdates as $oid => $entity) {
1092 119
            if ($this->em->getClassMetadata(get_class($entity))->name !== $className) {
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1093 77
                continue;
1094
            }
1095
1096 119
            if ($preUpdateInvoke != ListenersInvoker::INVOKE_NONE) {
1097 13
                $this->listenersInvoker->invoke($class, Events::preUpdate, $entity, new PreUpdateEventArgs($entity, $this->em, $this->getEntityChangeSet($entity)), $preUpdateInvoke);
1098
1099 13
                $this->recomputeSingleEntityChangeSet($class, $entity);
1100
            }
1101
1102 119
            if ( ! empty($this->entityChangeSets[$oid])) {
1103 85
                $persister->update($entity);
1104
            }
1105
1106 115
            unset($this->entityUpdates[$oid]);
1107
1108 115 View Code Duplication
            if ($postUpdateInvoke != ListenersInvoker::INVOKE_NONE) {
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...
1109 115
                $this->listenersInvoker->invoke($class, Events::postUpdate, $entity, new LifecycleEventArgs($entity, $this->em), $postUpdateInvoke);
1110
            }
1111
        }
1112 115
    }
1113
1114
    /**
1115
     * Executes all entity deletions for entities of the specified type.
1116
     *
1117
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
1118
     *
1119
     * @return void
1120
     */
1121 63
    private function executeDeletions($class)
1122
    {
1123 63
        $className  = $class->name;
1124 63
        $persister  = $this->getEntityPersister($className);
1125 63
        $invoke     = $this->listenersInvoker->getSubscribedSystems($class, Events::postRemove);
1126
1127 63
        foreach ($this->entityDeletions as $oid => $entity) {
1128 63
            if ($this->em->getClassMetadata(get_class($entity))->name !== $className) {
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1129 25
                continue;
1130
            }
1131
1132 63
            $persister->delete($entity);
1133
1134
            unset(
1135 63
                $this->entityDeletions[$oid],
1136 63
                $this->entityIdentifiers[$oid],
1137 63
                $this->originalEntityData[$oid],
1138 63
                $this->entityStates[$oid]
1139
            );
1140
1141
            // Entity with this $oid after deletion treated as NEW, even if the $oid
1142
            // is obtained by a new entity because the old one went out of scope.
1143
            //$this->entityStates[$oid] = self::STATE_NEW;
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1144 63
            if ( ! $class->isIdentifierNatural()) {
1145 53
                $class->reflFields[$class->identifier[0]]->setValue($entity, null);
1146
            }
1147
1148 63 View Code Duplication
            if ($invoke !== ListenersInvoker::INVOKE_NONE) {
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...
1149 63
                $this->listenersInvoker->invoke($class, Events::postRemove, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
1150
            }
1151
        }
1152 62
    }
1153
1154
    /**
1155
     * Gets the commit order.
1156
     *
1157
     * @param array|null $entityChangeSet
1158
     *
1159
     * @return array
1160
     */
1161 1043
    private function getCommitOrder(array $entityChangeSet = null)
1162
    {
1163 1043
        if ($entityChangeSet === null) {
1164 1043
            $entityChangeSet = array_merge($this->entityInsertions, $this->entityUpdates, $this->entityDeletions);
1165
        }
1166
1167 1043
        $calc = $this->getCommitOrderCalculator();
1168
1169
        // See if there are any new classes in the changeset, that are not in the
1170
        // commit order graph yet (don't have a node).
1171
        // We have to inspect changeSet to be able to correctly build dependencies.
1172
        // It is not possible to use IdentityMap here because post inserted ids
1173
        // are not yet available.
1174 1043
        $newNodes = [];
1175
1176 1043
        foreach ($entityChangeSet as $entity) {
1177 1043
            $class = $this->em->getClassMetadata(get_class($entity));
1178
1179 1043
            if ($calc->hasNode($class->name)) {
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1180 637
                continue;
1181
            }
1182
1183 1043
            $calc->addNode($class->name, $class);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1184
1185 1043
            $newNodes[] = $class;
1186
        }
1187
1188
        // Calculate dependencies for new nodes
1189 1043
        while ($class = array_pop($newNodes)) {
1190 1043
            foreach ($class->associationMappings as $assoc) {
1191 906
                if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) {
1192 863
                    continue;
1193
                }
1194
1195 857
                $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
1196
1197 857
                if ( ! $calc->hasNode($targetClass->name)) {
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1198 660
                    $calc->addNode($targetClass->name, $targetClass);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1199
1200 660
                    $newNodes[] = $targetClass;
1201
                }
1202
1203 857
                $joinColumns = reset($assoc['joinColumns']);
1204
1205 857
                $calc->addDependency($targetClass->name, $class->name, (int)empty($joinColumns['nullable']));
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1206
1207
                // If the target class has mapped subclasses, these share the same dependency.
1208 857
                if ( ! $targetClass->subClasses) {
0 ignored issues
show
Bug introduced by
Accessing subClasses 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...
1209 850
                    continue;
1210
                }
1211
1212 226
                foreach ($targetClass->subClasses as $subClassName) {
0 ignored issues
show
Bug introduced by
Accessing subClasses 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...
1213 226
                    $targetSubClass = $this->em->getClassMetadata($subClassName);
1214
1215 226
                    if ( ! $calc->hasNode($subClassName)) {
1216 196
                        $calc->addNode($targetSubClass->name, $targetSubClass);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1217
1218 196
                        $newNodes[] = $targetSubClass;
1219
                    }
1220
1221 226
                    $calc->addDependency($targetSubClass->name, $class->name, 1);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1222
                }
1223
            }
1224
        }
1225
1226 1043
        return $calc->sort();
1227
    }
1228
1229
    /**
1230
     * Schedules an entity for insertion into the database.
1231
     * If the entity already has an identifier, it will be added to the identity map.
1232
     *
1233
     * @param object $entity The entity to schedule for insertion.
1234
     *
1235
     * @return void
1236
     *
1237
     * @throws ORMInvalidArgumentException
1238
     * @throws \InvalidArgumentException
1239
     */
1240 1071
    public function scheduleForInsert($entity)
1241
    {
1242 1071
        $oid = spl_object_hash($entity);
1243
1244 1071
        if (isset($this->entityUpdates[$oid])) {
1245
            throw new InvalidArgumentException("Dirty entity can not be scheduled for insertion.");
1246
        }
1247
1248 1071
        if (isset($this->entityDeletions[$oid])) {
1249 1
            throw ORMInvalidArgumentException::scheduleInsertForRemovedEntity($entity);
1250
        }
1251 1071
        if (isset($this->originalEntityData[$oid]) && ! isset($this->entityInsertions[$oid])) {
1252 1
            throw ORMInvalidArgumentException::scheduleInsertForManagedEntity($entity);
1253
        }
1254
1255 1071
        if (isset($this->entityInsertions[$oid])) {
1256 1
            throw ORMInvalidArgumentException::scheduleInsertTwice($entity);
1257
        }
1258
1259 1071
        $this->entityInsertions[$oid] = $entity;
1260
1261 1071
        if (isset($this->entityIdentifiers[$oid])) {
1262 281
            $this->addToIdentityMap($entity);
1263
        }
1264
1265 1071
        if ($entity instanceof NotifyPropertyChanged) {
1266 7
            $entity->addPropertyChangedListener($this);
1267
        }
1268 1071
    }
1269
1270
    /**
1271
     * Checks whether an entity is scheduled for insertion.
1272
     *
1273
     * @param object $entity
1274
     *
1275
     * @return boolean
1276
     */
1277 645
    public function isScheduledForInsert($entity)
1278
    {
1279 645
        return isset($this->entityInsertions[spl_object_hash($entity)]);
1280
    }
1281
1282
    /**
1283
     * Schedules an entity for being updated.
1284
     *
1285
     * @param object $entity The entity to schedule for being updated.
1286
     *
1287
     * @return void
1288
     *
1289
     * @throws ORMInvalidArgumentException
1290
     */
1291 1
    public function scheduleForUpdate($entity)
1292
    {
1293 1
        $oid = spl_object_hash($entity);
1294
1295 1
        if ( ! isset($this->entityIdentifiers[$oid])) {
1296
            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "scheduling for update");
1297
        }
1298
1299 1
        if (isset($this->entityDeletions[$oid])) {
1300
            throw ORMInvalidArgumentException::entityIsRemoved($entity, "schedule for update");
1301
        }
1302
1303 1
        if ( ! isset($this->entityUpdates[$oid]) && ! isset($this->entityInsertions[$oid])) {
1304 1
            $this->entityUpdates[$oid] = $entity;
1305
        }
1306 1
    }
1307
1308
    /**
1309
     * INTERNAL:
1310
     * Schedules an extra update that will be executed immediately after the
1311
     * regular entity updates within the currently running commit cycle.
1312
     *
1313
     * Extra updates for entities are stored as (entity, changeset) tuples.
1314
     *
1315
     * @ignore
1316
     *
1317
     * @param object $entity    The entity for which to schedule an extra update.
1318
     * @param array  $changeset The changeset of the entity (what to update).
1319
     *
1320
     * @return void
1321
     */
1322 44
    public function scheduleExtraUpdate($entity, array $changeset)
1323
    {
1324 44
        $oid         = spl_object_hash($entity);
1325 44
        $extraUpdate = [$entity, $changeset];
1326
1327 44
        if (isset($this->extraUpdates[$oid])) {
1328 1
            list(, $changeset2) = $this->extraUpdates[$oid];
1329
1330 1
            $extraUpdate = [$entity, $changeset + $changeset2];
1331
        }
1332
1333 44
        $this->extraUpdates[$oid] = $extraUpdate;
1334 44
    }
1335
1336
    /**
1337
     * Checks whether an entity is registered as dirty in the unit of work.
1338
     * Note: Is not very useful currently as dirty entities are only registered
1339
     * at commit time.
1340
     *
1341
     * @param object $entity
1342
     *
1343
     * @return boolean
1344
     */
1345
    public function isScheduledForUpdate($entity)
1346
    {
1347
        return isset($this->entityUpdates[spl_object_hash($entity)]);
1348
    }
1349
1350
    /**
1351
     * Checks whether an entity is registered to be checked in the unit of work.
1352
     *
1353
     * @param object $entity
1354
     *
1355
     * @return boolean
1356
     */
1357 1
    public function isScheduledForDirtyCheck($entity)
1358
    {
1359 1
        $rootEntityName = $this->em->getClassMetadata(get_class($entity))->rootEntityName;
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
1360
1361 1
        return isset($this->scheduledForSynchronization[$rootEntityName][spl_object_hash($entity)]);
1362
    }
1363
1364
    /**
1365
     * INTERNAL:
1366
     * Schedules an entity for deletion.
1367
     *
1368
     * @param object $entity
1369
     *
1370
     * @return void
1371
     */
1372 66
    public function scheduleForDelete($entity)
1373
    {
1374 66
        $oid = spl_object_hash($entity);
1375
1376 66
        if (isset($this->entityInsertions[$oid])) {
1377 1
            if ($this->isInIdentityMap($entity)) {
1378
                $this->removeFromIdentityMap($entity);
1379
            }
1380
1381 1
            unset($this->entityInsertions[$oid], $this->entityStates[$oid]);
1382
1383 1
            return; // entity has not been persisted yet, so nothing more to do.
1384
        }
1385
1386 66
        if ( ! $this->isInIdentityMap($entity)) {
1387 1
            return;
1388
        }
1389
1390 65
        $this->removeFromIdentityMap($entity);
1391
1392 65
        unset($this->entityUpdates[$oid]);
1393
1394 65
        if ( ! isset($this->entityDeletions[$oid])) {
1395 65
            $this->entityDeletions[$oid] = $entity;
1396 65
            $this->entityStates[$oid]    = self::STATE_REMOVED;
1397
        }
1398 65
    }
1399
1400
    /**
1401
     * Checks whether an entity is registered as removed/deleted with the unit
1402
     * of work.
1403
     *
1404
     * @param object $entity
1405
     *
1406
     * @return boolean
1407
     */
1408 17
    public function isScheduledForDelete($entity)
1409
    {
1410 17
        return isset($this->entityDeletions[spl_object_hash($entity)]);
1411
    }
1412
1413
    /**
1414
     * Checks whether an entity is scheduled for insertion, update or deletion.
1415
     *
1416
     * @param object $entity
1417
     *
1418
     * @return boolean
1419
     */
1420
    public function isEntityScheduled($entity)
1421
    {
1422
        $oid = spl_object_hash($entity);
1423
1424
        return isset($this->entityInsertions[$oid])
1425
            || isset($this->entityUpdates[$oid])
1426
            || isset($this->entityDeletions[$oid]);
1427
    }
1428
1429
    /**
1430
     * INTERNAL:
1431
     * Registers an entity in the identity map.
1432
     * Note that entities in a hierarchy are registered with the class name of
1433
     * the root entity.
1434
     *
1435
     * @ignore
1436
     *
1437
     * @param object $entity The entity to register.
1438
     *
1439
     * @return boolean TRUE if the registration was successful, FALSE if the identity of
1440
     *                 the entity in question is already managed.
1441
     *
1442
     * @throws ORMInvalidArgumentException
1443
     */
1444 1137
    public function addToIdentityMap($entity)
1445
    {
1446 1137
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1447 1137
        $identifier    = $this->entityIdentifiers[spl_object_hash($entity)];
1448
1449 1137
        if (empty($identifier) || in_array(null, $identifier, true)) {
1450 6
            throw ORMInvalidArgumentException::entityWithoutIdentity($classMetadata->name, $entity);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1451
        }
1452
1453 1131
        $idHash    = implode(' ', $identifier);
1454 1131
        $className = $classMetadata->rootEntityName;
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
1455
1456 1131
        if (isset($this->identityMap[$className][$idHash])) {
1457 86
            return false;
1458
        }
1459
1460 1131
        $this->identityMap[$className][$idHash] = $entity;
1461
1462 1131
        return true;
1463
    }
1464
1465
    /**
1466
     * Gets the state of an entity with regard to the current unit of work.
1467
     *
1468
     * @param object   $entity
1469
     * @param int|null $assume The state to assume if the state is not yet known (not MANAGED or REMOVED).
1470
     *                         This parameter can be set to improve performance of entity state detection
1471
     *                         by potentially avoiding a database lookup if the distinction between NEW and DETACHED
1472
     *                         is either known or does not matter for the caller of the method.
1473
     *
1474
     * @return int The entity state.
1475
     */
1476 1085
    public function getEntityState($entity, $assume = null)
1477
    {
1478 1085
        $oid = spl_object_hash($entity);
1479
1480 1085
        if (isset($this->entityStates[$oid])) {
1481 801
            return $this->entityStates[$oid];
1482
        }
1483
1484 1079
        if ($assume !== null) {
1485 1075
            return $assume;
1486
        }
1487
1488
        // State can only be NEW or DETACHED, because MANAGED/REMOVED states are known.
1489
        // Note that you can not remember the NEW or DETACHED state in _entityStates since
1490
        // the UoW does not hold references to such objects and the object hash can be reused.
1491
        // More generally because the state may "change" between NEW/DETACHED without the UoW being aware of it.
1492 13
        $class = $this->em->getClassMetadata(get_class($entity));
1493 13
        $id    = $class->getIdentifierValues($entity);
1494
1495 13
        if ( ! $id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1496 5
            return self::STATE_NEW;
1497
        }
1498
1499 10
        if ($class->containsForeignIdentifier) {
0 ignored issues
show
Bug introduced by
Accessing containsForeignIdentifier 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...
1500 1
            $id = $this->identifierFlattener->flattenIdentifier($class, $id);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
1501
        }
1502
1503
        switch (true) {
1504 10
            case ($class->isIdentifierNatural()):
0 ignored issues
show
Bug introduced by
The method isIdentifierNatural() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. Did you maybe mean isIdentifier()?

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...
1505
                // Check for a version field, if available, to avoid a db lookup.
1506 5
                if ($class->isVersioned) {
0 ignored issues
show
Bug introduced by
Accessing isVersioned 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...
1507 1
                    return ($class->getFieldValue($entity, $class->versionField))
0 ignored issues
show
Bug introduced by
Accessing versionField 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...
1508
                        ? self::STATE_DETACHED
1509 1
                        : self::STATE_NEW;
1510
                }
1511
1512
                // Last try before db lookup: check the identity map.
1513 4
                if ($this->tryGetById($id, $class->rootEntityName)) {
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
1514 1
                    return self::STATE_DETACHED;
1515
                }
1516
1517
                // db lookup
1518 4
                if ($this->getEntityPersister($class->name)->exists($entity)) {
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1519
                    return self::STATE_DETACHED;
1520
                }
1521
1522 4
                return self::STATE_NEW;
1523
1524 5
            case ( ! $class->idGenerator->isPostInsertGenerator()):
0 ignored issues
show
Bug introduced by
Accessing idGenerator 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...
1525
                // if we have a pre insert generator we can't be sure that having an id
1526
                // really means that the entity exists. We have to verify this through
1527
                // the last resort: a db lookup
1528
1529
                // Last try before db lookup: check the identity map.
1530
                if ($this->tryGetById($id, $class->rootEntityName)) {
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
1531
                    return self::STATE_DETACHED;
1532
                }
1533
1534
                // db lookup
1535
                if ($this->getEntityPersister($class->name)->exists($entity)) {
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1536
                    return self::STATE_DETACHED;
1537
                }
1538
1539
                return self::STATE_NEW;
1540
1541
            default:
1542 5
                return self::STATE_DETACHED;
1543
        }
1544
    }
1545
1546
    /**
1547
     * INTERNAL:
1548
     * Removes an entity from the identity map. This effectively detaches the
1549
     * entity from the persistence management of Doctrine.
1550
     *
1551
     * @ignore
1552
     *
1553
     * @param object $entity
1554
     *
1555
     * @return boolean
1556
     *
1557
     * @throws ORMInvalidArgumentException
1558
     */
1559 78
    public function removeFromIdentityMap($entity)
1560
    {
1561 78
        $oid           = spl_object_hash($entity);
1562 78
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1563 78
        $idHash        = implode(' ', $this->entityIdentifiers[$oid]);
1564
1565 78
        if ($idHash === '') {
1566
            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "remove from identity map");
1567
        }
1568
1569 78
        $className = $classMetadata->rootEntityName;
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
1570
1571 78
        if (isset($this->identityMap[$className][$idHash])) {
1572 78
            unset($this->identityMap[$className][$idHash]);
1573 78
            unset($this->readOnlyObjects[$oid]);
1574
1575
            //$this->entityStates[$oid] = self::STATE_DETACHED;
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1576
1577 78
            return true;
1578
        }
1579
1580
        return false;
1581
    }
1582
1583
    /**
1584
     * INTERNAL:
1585
     * Gets an entity in the identity map by its identifier hash.
1586
     *
1587
     * @ignore
1588
     *
1589
     * @param string $idHash
1590
     * @param string $rootClassName
1591
     *
1592
     * @return object
1593
     */
1594 6
    public function getByIdHash($idHash, $rootClassName)
1595
    {
1596 6
        return $this->identityMap[$rootClassName][$idHash];
1597
    }
1598
1599
    /**
1600
     * INTERNAL:
1601
     * Tries to get an entity by its identifier hash. If no entity is found for
1602
     * the given hash, FALSE is returned.
1603
     *
1604
     * @ignore
1605
     *
1606
     * @param mixed  $idHash        (must be possible to cast it to string)
1607
     * @param string $rootClassName
1608
     *
1609
     * @return object|bool The found entity or FALSE.
1610
     */
1611 35 View Code Duplication
    public function tryGetByIdHash($idHash, $rootClassName)
1612
    {
1613 35
        $stringIdHash = (string) $idHash;
1614
1615 35
        return isset($this->identityMap[$rootClassName][$stringIdHash])
1616 35
            ? $this->identityMap[$rootClassName][$stringIdHash]
1617 35
            : false;
1618
    }
1619
1620
    /**
1621
     * Checks whether an entity is registered in the identity map of this UnitOfWork.
1622
     *
1623
     * @param object $entity
1624
     *
1625
     * @return boolean
1626
     */
1627 216
    public function isInIdentityMap($entity)
1628
    {
1629 216
        $oid = spl_object_hash($entity);
1630
1631 216
        if (empty($this->entityIdentifiers[$oid])) {
1632 33
            return false;
1633
        }
1634
1635 200
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1636 200
        $idHash        = implode(' ', $this->entityIdentifiers[$oid]);
1637
1638 200
        return isset($this->identityMap[$classMetadata->rootEntityName][$idHash]);
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
1639
    }
1640
1641
    /**
1642
     * INTERNAL:
1643
     * Checks whether an identifier hash exists in the identity map.
1644
     *
1645
     * @ignore
1646
     *
1647
     * @param string $idHash
1648
     * @param string $rootClassName
1649
     *
1650
     * @return boolean
1651
     */
1652
    public function containsIdHash($idHash, $rootClassName)
1653
    {
1654
        return isset($this->identityMap[$rootClassName][$idHash]);
1655
    }
1656
1657
    /**
1658
     * Persists an entity as part of the current unit of work.
1659
     *
1660
     * @param object $entity The entity to persist.
1661
     *
1662
     * @return void
1663
     */
1664 1066
    public function persist($entity)
1665
    {
1666 1066
        $visited = [];
1667
1668 1066
        $this->doPersist($entity, $visited);
1669 1059
    }
1670
1671
    /**
1672
     * Persists an entity as part of the current unit of work.
1673
     *
1674
     * This method is internally called during persist() cascades as it tracks
1675
     * the already visited entities to prevent infinite recursions.
1676
     *
1677
     * @param object $entity  The entity to persist.
1678
     * @param array  $visited The already visited entities.
1679
     *
1680
     * @return void
1681
     *
1682
     * @throws ORMInvalidArgumentException
1683
     * @throws UnexpectedValueException
1684
     */
1685 1066
    private function doPersist($entity, array &$visited)
1686
    {
1687 1066
        $oid = spl_object_hash($entity);
1688
1689 1066
        if (isset($visited[$oid])) {
1690 110
            return; // Prevent infinite recursion
1691
        }
1692
1693 1066
        $visited[$oid] = $entity; // Mark visited
1694
1695 1066
        $class = $this->em->getClassMetadata(get_class($entity));
1696
1697
        // We assume NEW, so DETACHED entities result in an exception on flush (constraint violation).
1698
        // If we would detect DETACHED here we would throw an exception anyway with the same
1699
        // consequences (not recoverable/programming error), so just assuming NEW here
1700
        // lets us avoid some database lookups for entities with natural identifiers.
1701 1066
        $entityState = $this->getEntityState($entity, self::STATE_NEW);
1702
1703
        switch ($entityState) {
1704 1066
            case self::STATE_MANAGED:
1705
                // Nothing to do, except if policy is "deferred explicit"
1706 238
                if ($class->isChangeTrackingDeferredExplicit()) {
1707 2
                    $this->scheduleForDirtyCheck($entity);
1708
                }
1709 238
                break;
1710
1711 1066
            case self::STATE_NEW:
1712 1065
                $this->persistNew($class, $entity);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
1713 1065
                break;
1714
1715 1
            case self::STATE_REMOVED:
1716
                // Entity becomes managed again
1717 1
                unset($this->entityDeletions[$oid]);
1718 1
                $this->addToIdentityMap($entity);
1719
1720 1
                $this->entityStates[$oid] = self::STATE_MANAGED;
1721 1
                break;
1722
1723
            case self::STATE_DETACHED:
1724
                // Can actually not happen right now since we assume STATE_NEW.
1725
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "persisted");
1726
1727
            default:
1728
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1729
        }
1730
1731 1066
        $this->cascadePersist($entity, $visited);
1732 1059
    }
1733
1734
    /**
1735
     * Deletes an entity as part of the current unit of work.
1736
     *
1737
     * @param object $entity The entity to remove.
1738
     *
1739
     * @return void
1740
     */
1741 65
    public function remove($entity)
1742
    {
1743 65
        $visited = [];
1744
1745 65
        $this->doRemove($entity, $visited);
1746 65
    }
1747
1748
    /**
1749
     * Deletes an entity as part of the current unit of work.
1750
     *
1751
     * This method is internally called during delete() cascades as it tracks
1752
     * the already visited entities to prevent infinite recursions.
1753
     *
1754
     * @param object $entity  The entity to delete.
1755
     * @param array  $visited The map of the already visited entities.
1756
     *
1757
     * @return void
1758
     *
1759
     * @throws ORMInvalidArgumentException If the instance is a detached entity.
1760
     * @throws UnexpectedValueException
1761
     */
1762 65
    private function doRemove($entity, array &$visited)
1763
    {
1764 65
        $oid = spl_object_hash($entity);
1765
1766 65
        if (isset($visited[$oid])) {
1767 1
            return; // Prevent infinite recursion
1768
        }
1769
1770 65
        $visited[$oid] = $entity; // mark visited
1771
1772
        // Cascade first, because scheduleForDelete() removes the entity from the identity map, which
1773
        // can cause problems when a lazy proxy has to be initialized for the cascade operation.
1774 65
        $this->cascadeRemove($entity, $visited);
1775
1776 65
        $class       = $this->em->getClassMetadata(get_class($entity));
1777 65
        $entityState = $this->getEntityState($entity);
1778
1779
        switch ($entityState) {
1780 65
            case self::STATE_NEW:
1781 65
            case self::STATE_REMOVED:
1782
                // nothing to do
1783 2
                break;
1784
1785 65
            case self::STATE_MANAGED:
1786 65
                $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preRemove);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
1787
1788 65 View Code Duplication
                if ($invoke !== ListenersInvoker::INVOKE_NONE) {
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...
1789 8
                    $this->listenersInvoker->invoke($class, Events::preRemove, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
1790
                }
1791
1792 65
                $this->scheduleForDelete($entity);
1793 65
                break;
1794
1795
            case self::STATE_DETACHED:
1796
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "removed");
1797
            default:
1798
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1799
        }
1800
1801 65
    }
1802
1803
    /**
1804
     * Merges the state of the given detached entity into this UnitOfWork.
1805
     *
1806
     * @param object $entity
1807
     *
1808
     * @return object The managed copy of the entity.
1809
     *
1810
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1811
     *         attribute and the version check against the managed copy fails.
1812
     *
1813
     * @todo Require active transaction!? OptimisticLockException may result in undefined state!?
1814
     */
1815 43
    public function merge($entity)
1816
    {
1817 43
        $visited = [];
1818
1819 43
        return $this->doMerge($entity, $visited);
1820
    }
1821
1822
    /**
1823
     * Executes a merge operation on an entity.
1824
     *
1825
     * @param object      $entity
1826
     * @param array       $visited
1827
     * @param object|null $prevManagedCopy
1828
     * @param array|null  $assoc
1829
     *
1830
     * @return object The managed copy of the entity.
1831
     *
1832
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1833
     *         attribute and the version check against the managed copy fails.
1834
     * @throws ORMInvalidArgumentException If the entity instance is NEW.
1835
     * @throws EntityNotFoundException if an assigned identifier is used in the entity, but none is provided
1836
     */
1837 43
    private function doMerge($entity, array &$visited, $prevManagedCopy = null, array $assoc = [])
1838
    {
1839 43
        $oid = spl_object_hash($entity);
1840
1841 43
        if (isset($visited[$oid])) {
1842 4
            $managedCopy = $visited[$oid];
1843
1844 4
            if ($prevManagedCopy !== null) {
1845 4
                $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy);
1846
            }
1847
1848 4
            return $managedCopy;
1849
        }
1850
1851 43
        $class = $this->em->getClassMetadata(get_class($entity));
1852
1853
        // First we assume DETACHED, although it can still be NEW but we can avoid
1854
        // an extra db-roundtrip this way. If it is not MANAGED but has an identity,
1855
        // we need to fetch it from the db anyway in order to merge.
1856
        // MANAGED entities are ignored by the merge operation.
1857 43
        $managedCopy = $entity;
1858
1859 43
        if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) {
1860
            // Try to look the entity up in the identity map.
1861 42
            $id = $class->getIdentifierValues($entity);
1862
1863
            // If there is no ID, it is actually NEW.
1864 42
            if ( ! $id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1865 6
                $managedCopy = $this->newInstance($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\ORM\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...
1866
1867 6
                $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy);
1868 6
                $this->persistNew($class, $managedCopy);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
1869
            } else {
1870 37
                $flatId = ($class->containsForeignIdentifier)
0 ignored issues
show
Bug introduced by
Accessing containsForeignIdentifier 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...
1871 3
                    ? $this->identifierFlattener->flattenIdentifier($class, $id)
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
1872 37
                    : $id;
1873
1874 37
                $managedCopy = $this->tryGetById($flatId, $class->rootEntityName);
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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 Compatibility introduced by
The expression $this->tryGetById($flatI...class->rootEntityName); of type object|boolean adds the type boolean to the return on line 1923 which is incompatible with the return type documented by Doctrine\ORM\UnitOfWork::doMerge of type object.
Loading history...
1875
1876 37
                if ($managedCopy) {
1877
                    // We have the entity in-memory already, just make sure its not removed.
1878 15
                    if ($this->getEntityState($managedCopy) == self::STATE_REMOVED) {
0 ignored issues
show
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1874 can also be of type boolean; however, Doctrine\ORM\UnitOfWork::getEntityState() does only seem to accept object, 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...
1879 15
                        throw ORMInvalidArgumentException::entityIsRemoved($managedCopy, "merge");
0 ignored issues
show
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1874 can also be of type boolean; however, Doctrine\ORM\ORMInvalidA...tion::entityIsRemoved() does only seem to accept object, 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...
1880
                    }
1881
                } else {
1882
                    // We need to fetch the managed copy in order to merge.
1883 25
                    $managedCopy = $this->em->find($class->name, $flatId);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1884
                }
1885
1886 37
                if ($managedCopy === null) {
1887
                    // If the identifier is ASSIGNED, it is NEW, otherwise an error
1888
                    // since the managed entity was not found.
1889 3
                    if ( ! $class->isIdentifierNatural()) {
0 ignored issues
show
Bug introduced by
The method isIdentifierNatural() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. Did you maybe mean isIdentifier()?

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...
1890 1
                        throw EntityNotFoundException::fromClassNameAndIdentifier(
1891 1
                            $class->getName(),
1892 1
                            $this->identifierFlattener->flattenIdentifier($class, $id)
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
1893
                        );
1894
                    }
1895
1896 2
                    $managedCopy = $this->newInstance($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\ORM\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...
1897 2
                    $class->setIdentifierValues($managedCopy, $id);
1898
1899 2
                    $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy);
1900 2
                    $this->persistNew($class, $managedCopy);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
1901
                } else {
1902 34
                    $this->ensureVersionMatch($class, $entity, $managedCopy);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1874 can also be of type boolean; however, Doctrine\ORM\UnitOfWork::ensureVersionMatch() does only seem to accept object, 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...
1903 33
                    $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy);
0 ignored issues
show
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1874 can also be of type boolean; however, Doctrine\ORM\UnitOfWork:...yStateIntoManagedCopy() does only seem to accept object, 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...
1904
                }
1905
            }
1906
1907 40
            $visited[$oid] = $managedCopy; // mark visited
1908
1909 40
            if ($class->isChangeTrackingDeferredExplicit()) {
1910
                $this->scheduleForDirtyCheck($entity);
1911
            }
1912
        }
1913
1914 41
        if ($prevManagedCopy !== null) {
1915 6
            $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy);
0 ignored issues
show
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1874 can also be of type boolean; however, Doctrine\ORM\UnitOfWork:...ationWithMergedEntity() does only seem to accept object, 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...
1916
        }
1917
1918
        // Mark the managed copy visited as well
1919 41
        $visited[spl_object_hash($managedCopy)] = $managedCopy;
1920
1921 41
        $this->cascadeMerge($entity, $managedCopy, $visited);
0 ignored issues
show
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1874 can also be of type boolean; however, Doctrine\ORM\UnitOfWork::cascadeMerge() does only seem to accept object, 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...
1922
1923 41
        return $managedCopy;
1924
    }
1925
1926
    /**
1927
     * @param ClassMetadata $class
1928
     * @param object        $entity
1929
     * @param object        $managedCopy
1930
     *
1931
     * @return void
1932
     *
1933
     * @throws OptimisticLockException
1934
     */
1935 34
    private function ensureVersionMatch(ClassMetadata $class, $entity, $managedCopy)
1936
    {
1937 34
        if (! ($class->isVersioned && $this->isLoaded($managedCopy) && $this->isLoaded($entity))) {
1938 31
            return;
1939
        }
1940
1941 4
        $reflField          = $class->reflFields[$class->versionField];
1942 4
        $managedCopyVersion = $reflField->getValue($managedCopy);
1943 4
        $entityVersion      = $reflField->getValue($entity);
1944
1945
        // Throw exception if versions don't match.
1946 4
        if ($managedCopyVersion == $entityVersion) {
1947 3
            return;
1948
        }
1949
1950 1
        throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion);
1951
    }
1952
1953
    /**
1954
     * Tests if an entity is loaded - must either be a loaded proxy or not a proxy
1955
     *
1956
     * @param object $entity
1957
     *
1958
     * @return bool
1959
     */
1960 41
    private function isLoaded($entity)
1961
    {
1962 41
        return !($entity instanceof Proxy) || $entity->__isInitialized();
1963
    }
1964
1965
    /**
1966
     * Sets/adds associated managed copies into the previous entity's association field
1967
     *
1968
     * @param object $entity
1969
     * @param array  $association
1970
     * @param object $previousManagedCopy
1971
     * @param object $managedCopy
1972
     *
1973
     * @return void
1974
     */
1975 6
    private function updateAssociationWithMergedEntity($entity, array $association, $previousManagedCopy, $managedCopy)
1976
    {
1977 6
        $assocField = $association['fieldName'];
1978 6
        $prevClass  = $this->em->getClassMetadata(get_class($previousManagedCopy));
1979
1980 6
        if ($association['type'] & ClassMetadata::TO_ONE) {
1981 6
            $prevClass->reflFields[$assocField]->setValue($previousManagedCopy, $managedCopy);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
1982
1983 6
            return;
1984
        }
1985
1986 1
        $value   = $prevClass->reflFields[$assocField]->getValue($previousManagedCopy);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
1987 1
        $value[] = $managedCopy;
1988
1989 1 View Code Duplication
        if ($association['type'] == ClassMetadata::ONE_TO_MANY) {
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...
1990 1
            $class = $this->em->getClassMetadata(get_class($entity));
1991
1992 1
            $class->reflFields[$association['mappedBy']]->setValue($managedCopy, $previousManagedCopy);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
1993
        }
1994 1
    }
1995
1996
    /**
1997
     * Detaches an entity from the persistence management. It's persistence will
1998
     * no longer be managed by Doctrine.
1999
     *
2000
     * @param object $entity The entity to detach.
2001
     *
2002
     * @return void
2003
     */
2004 12
    public function detach($entity)
2005
    {
2006 12
        $visited = [];
2007
2008 12
        $this->doDetach($entity, $visited);
2009 12
    }
2010
2011
    /**
2012
     * Executes a detach operation on the given entity.
2013
     *
2014
     * @param object  $entity
2015
     * @param array   $visited
2016
     * @param boolean $noCascade if true, don't cascade detach operation.
2017
     *
2018
     * @return void
2019
     */
2020 16
    private function doDetach($entity, array &$visited, $noCascade = false)
2021
    {
2022 16
        $oid = spl_object_hash($entity);
2023
2024 16
        if (isset($visited[$oid])) {
2025
            return; // Prevent infinite recursion
2026
        }
2027
2028 16
        $visited[$oid] = $entity; // mark visited
2029
2030 16
        switch ($this->getEntityState($entity, self::STATE_DETACHED)) {
2031 16
            case self::STATE_MANAGED:
2032 14
                if ($this->isInIdentityMap($entity)) {
2033 13
                    $this->removeFromIdentityMap($entity);
2034
                }
2035
2036
                unset(
2037 14
                    $this->entityInsertions[$oid],
2038 14
                    $this->entityUpdates[$oid],
2039 14
                    $this->entityDeletions[$oid],
2040 14
                    $this->entityIdentifiers[$oid],
2041 14
                    $this->entityStates[$oid],
2042 14
                    $this->originalEntityData[$oid]
2043
                );
2044 14
                break;
2045 3
            case self::STATE_NEW:
2046 3
            case self::STATE_DETACHED:
2047 3
                return;
2048
        }
2049
2050 14
        if ( ! $noCascade) {
2051 14
            $this->cascadeDetach($entity, $visited);
2052
        }
2053 14
    }
2054
2055
    /**
2056
     * Refreshes the state of the given entity from the database, overwriting
2057
     * any local, unpersisted changes.
2058
     *
2059
     * @param object $entity The entity to refresh.
2060
     *
2061
     * @return void
2062
     *
2063
     * @throws InvalidArgumentException If the entity is not MANAGED.
2064
     */
2065 17
    public function refresh($entity)
2066
    {
2067 17
        $visited = [];
2068
2069 17
        $this->doRefresh($entity, $visited);
2070 17
    }
2071
2072
    /**
2073
     * Executes a refresh operation on an entity.
2074
     *
2075
     * @param object $entity  The entity to refresh.
2076
     * @param array  $visited The already visited entities during cascades.
2077
     *
2078
     * @return void
2079
     *
2080
     * @throws ORMInvalidArgumentException If the entity is not MANAGED.
2081
     */
2082 17
    private function doRefresh($entity, array &$visited)
2083
    {
2084 17
        $oid = spl_object_hash($entity);
2085
2086 17
        if (isset($visited[$oid])) {
2087
            return; // Prevent infinite recursion
2088
        }
2089
2090 17
        $visited[$oid] = $entity; // mark visited
2091
2092 17
        $class = $this->em->getClassMetadata(get_class($entity));
2093
2094 17
        if ($this->getEntityState($entity) !== self::STATE_MANAGED) {
2095
            throw ORMInvalidArgumentException::entityNotManaged($entity);
2096
        }
2097
2098 17
        $this->getEntityPersister($class->name)->refresh(
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2099 17
            array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
2100 17
            $entity
2101
        );
2102
2103 17
        $this->cascadeRefresh($entity, $visited);
2104 17
    }
2105
2106
    /**
2107
     * Cascades a refresh operation to associated entities.
2108
     *
2109
     * @param object $entity
2110
     * @param array  $visited
2111
     *
2112
     * @return void
2113
     */
2114 17 View Code Duplication
    private function cascadeRefresh($entity, array &$visited)
2115
    {
2116 17
        $class = $this->em->getClassMetadata(get_class($entity));
2117
2118 17
        $associationMappings = array_filter(
2119 17
            $class->associationMappings,
0 ignored issues
show
Bug introduced by
Accessing associationMappings 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...
2120
            function ($assoc) { return $assoc['isCascadeRefresh']; }
2121
        );
2122
2123 17
        foreach ($associationMappings as $assoc) {
2124 5
            $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
2125
2126
            switch (true) {
2127 5
                case ($relatedEntities instanceof PersistentCollection):
2128
                    // Unwrap so that foreach() does not initialize
2129 5
                    $relatedEntities = $relatedEntities->unwrap();
2130
                    // break; is commented intentionally!
2131
2132
                case ($relatedEntities instanceof Collection):
2133
                case (is_array($relatedEntities)):
2134 5
                    foreach ($relatedEntities as $relatedEntity) {
2135
                        $this->doRefresh($relatedEntity, $visited);
2136
                    }
2137 5
                    break;
2138
2139
                case ($relatedEntities !== null):
2140
                    $this->doRefresh($relatedEntities, $visited);
2141
                    break;
2142
2143 5
                default:
2144
                    // Do nothing
2145
            }
2146
        }
2147 17
    }
2148
2149
    /**
2150
     * Cascades a detach operation to associated entities.
2151
     *
2152
     * @param object $entity
2153
     * @param array  $visited
2154
     *
2155
     * @return void
2156
     */
2157 14 View Code Duplication
    private function cascadeDetach($entity, array &$visited)
2158
    {
2159 14
        $class = $this->em->getClassMetadata(get_class($entity));
2160
2161 14
        $associationMappings = array_filter(
2162 14
            $class->associationMappings,
0 ignored issues
show
Bug introduced by
Accessing associationMappings 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...
2163
            function ($assoc) { return $assoc['isCascadeDetach']; }
2164
        );
2165
2166 14
        foreach ($associationMappings as $assoc) {
2167 3
            $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
2168
2169
            switch (true) {
2170 3
                case ($relatedEntities instanceof PersistentCollection):
2171
                    // Unwrap so that foreach() does not initialize
2172 2
                    $relatedEntities = $relatedEntities->unwrap();
2173
                    // break; is commented intentionally!
2174
2175 1
                case ($relatedEntities instanceof Collection):
2176
                case (is_array($relatedEntities)):
2177 3
                    foreach ($relatedEntities as $relatedEntity) {
2178 1
                        $this->doDetach($relatedEntity, $visited);
2179
                    }
2180 3
                    break;
2181
2182
                case ($relatedEntities !== null):
2183
                    $this->doDetach($relatedEntities, $visited);
2184
                    break;
2185
2186 3
                default:
2187
                    // Do nothing
2188
            }
2189
        }
2190 14
    }
2191
2192
    /**
2193
     * Cascades a merge operation to associated entities.
2194
     *
2195
     * @param object $entity
2196
     * @param object $managedCopy
2197
     * @param array  $visited
2198
     *
2199
     * @return void
2200
     */
2201 41
    private function cascadeMerge($entity, $managedCopy, array &$visited)
2202
    {
2203 41
        $class = $this->em->getClassMetadata(get_class($entity));
2204
2205 41
        $associationMappings = array_filter(
2206 41
            $class->associationMappings,
0 ignored issues
show
Bug introduced by
Accessing associationMappings 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...
2207
            function ($assoc) { return $assoc['isCascadeMerge']; }
2208
        );
2209
2210 41
        foreach ($associationMappings as $assoc) {
2211 16
            $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
2212
2213 16
            if ($relatedEntities instanceof Collection) {
2214 10
                if ($relatedEntities === $class->reflFields[$assoc['fieldName']]->getValue($managedCopy)) {
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
2215 1
                    continue;
2216
                }
2217
2218 9
                if ($relatedEntities instanceof PersistentCollection) {
2219
                    // Unwrap so that foreach() does not initialize
2220 5
                    $relatedEntities = $relatedEntities->unwrap();
2221
                }
2222
2223 9
                foreach ($relatedEntities as $relatedEntity) {
2224 9
                    $this->doMerge($relatedEntity, $visited, $managedCopy, $assoc);
2225
                }
2226 7
            } else if ($relatedEntities !== null) {
2227 15
                $this->doMerge($relatedEntities, $visited, $managedCopy, $assoc);
2228
            }
2229
        }
2230 41
    }
2231
2232
    /**
2233
     * Cascades the save operation to associated entities.
2234
     *
2235
     * @param object $entity
2236
     * @param array  $visited
2237
     *
2238
     * @return void
2239
     */
2240 1066
    private function cascadePersist($entity, array &$visited)
2241
    {
2242 1066
        $class = $this->em->getClassMetadata(get_class($entity));
2243
2244 1066
        $associationMappings = array_filter(
2245 1066
            $class->associationMappings,
0 ignored issues
show
Bug introduced by
Accessing associationMappings 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...
2246
            function ($assoc) { return $assoc['isCascadePersist']; }
2247
        );
2248
2249 1066
        foreach ($associationMappings as $assoc) {
2250 663
            $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
2251
2252
            switch (true) {
2253 663
                case ($relatedEntities instanceof PersistentCollection):
2254
                    // Unwrap so that foreach() does not initialize
2255 21
                    $relatedEntities = $relatedEntities->unwrap();
2256
                    // break; is commented intentionally!
2257
2258 663
                case ($relatedEntities instanceof Collection):
2259 602
                case (is_array($relatedEntities)):
2260 565
                    if (($assoc['type'] & ClassMetadata::TO_MANY) <= 0) {
2261 3
                        throw ORMInvalidArgumentException::invalidAssociation(
2262 3
                            $this->em->getClassMetadata($assoc['targetEntity']),
0 ignored issues
show
Compatibility introduced by
$this->em->getClassMetad...$assoc['targetEntity']) of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
2263 3
                            $assoc,
2264 3
                            $relatedEntities
2265
                        );
2266
                    }
2267
2268 562
                    foreach ($relatedEntities as $relatedEntity) {
2269 283
                        $this->doPersist($relatedEntity, $visited);
2270
                    }
2271
2272 562
                    break;
2273
2274 592
                case ($relatedEntities !== null):
2275 253
                    if (! $relatedEntities instanceof $assoc['targetEntity']) {
2276 4
                        throw ORMInvalidArgumentException::invalidAssociation(
2277 4
                            $this->em->getClassMetadata($assoc['targetEntity']),
0 ignored issues
show
Compatibility introduced by
$this->em->getClassMetad...$assoc['targetEntity']) of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
2278 4
                            $assoc,
2279 4
                            $relatedEntities
2280
                        );
2281
                    }
2282
2283 249
                    $this->doPersist($relatedEntities, $visited);
2284 249
                    break;
2285
2286 657
                default:
2287
                    // Do nothing
2288
            }
2289
        }
2290 1059
    }
2291
2292
    /**
2293
     * Cascades the delete operation to associated entities.
2294
     *
2295
     * @param object $entity
2296
     * @param array  $visited
2297
     *
2298
     * @return void
2299
     */
2300 65
    private function cascadeRemove($entity, array &$visited)
2301
    {
2302 65
        $class = $this->em->getClassMetadata(get_class($entity));
2303
2304 65
        $associationMappings = array_filter(
2305 65
            $class->associationMappings,
0 ignored issues
show
Bug introduced by
Accessing associationMappings 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...
2306
            function ($assoc) { return $assoc['isCascadeRemove']; }
2307
        );
2308
2309 65
        $entitiesToCascade = [];
2310
2311 65
        foreach ($associationMappings as $assoc) {
2312 26
            if ($entity instanceof Proxy && !$entity->__isInitialized__) {
0 ignored issues
show
Bug introduced by
Accessing __isInitialized__ on the interface Doctrine\ORM\Proxy\Proxy 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...
2313 6
                $entity->__load();
2314
            }
2315
2316 26
            $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
2317
2318
            switch (true) {
2319 26
                case ($relatedEntities instanceof Collection):
2320 19
                case (is_array($relatedEntities)):
2321
                    // If its a PersistentCollection initialization is intended! No unwrap!
2322 20
                    foreach ($relatedEntities as $relatedEntity) {
2323 10
                        $entitiesToCascade[] = $relatedEntity;
2324
                    }
2325 20
                    break;
2326
2327 19
                case ($relatedEntities !== null):
2328 7
                    $entitiesToCascade[] = $relatedEntities;
2329 7
                    break;
2330
2331 26
                default:
2332
                    // Do nothing
2333
            }
2334
        }
2335
2336 65
        foreach ($entitiesToCascade as $relatedEntity) {
2337 16
            $this->doRemove($relatedEntity, $visited);
2338
        }
2339 65
    }
2340
2341
    /**
2342
     * Acquire a lock on the given entity.
2343
     *
2344
     * @param object $entity
2345
     * @param int    $lockMode
2346
     * @param int    $lockVersion
2347
     *
2348
     * @return void
2349
     *
2350
     * @throws ORMInvalidArgumentException
2351
     * @throws TransactionRequiredException
2352
     * @throws OptimisticLockException
2353
     */
2354 10
    public function lock($entity, $lockMode, $lockVersion = null)
2355
    {
2356 10
        if ($entity === null) {
2357 1
            throw new \InvalidArgumentException("No entity passed to UnitOfWork#lock().");
2358
        }
2359
2360 9
        if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) {
2361 1
            throw ORMInvalidArgumentException::entityNotManaged($entity);
2362
        }
2363
2364 8
        $class = $this->em->getClassMetadata(get_class($entity));
2365
2366
        switch (true) {
2367 8
            case LockMode::OPTIMISTIC === $lockMode:
2368 6
                if ( ! $class->isVersioned) {
0 ignored issues
show
Bug introduced by
Accessing isVersioned 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...
2369 2
                    throw OptimisticLockException::notVersioned($class->name);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2370
                }
2371
2372 4
                if ($lockVersion === null) {
2373
                    return;
2374
                }
2375
2376 4
                if ($entity instanceof Proxy && !$entity->__isInitialized__) {
0 ignored issues
show
Bug introduced by
Accessing __isInitialized__ on the interface Doctrine\ORM\Proxy\Proxy 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...
2377 1
                    $entity->__load();
2378
                }
2379
2380 4
                $entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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 versionField 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...
2381
2382 4
                if ($entityVersion != $lockVersion) {
2383 2
                    throw OptimisticLockException::lockFailedVersionMismatch($entity, $lockVersion, $entityVersion);
2384
                }
2385
2386 2
                break;
2387
2388 2
            case LockMode::NONE === $lockMode:
2389 2
            case LockMode::PESSIMISTIC_READ === $lockMode:
2390 1
            case LockMode::PESSIMISTIC_WRITE === $lockMode:
2391 2
                if (!$this->em->getConnection()->isTransactionActive()) {
2392 2
                    throw TransactionRequiredException::transactionRequired();
2393
                }
2394
2395
                $oid = spl_object_hash($entity);
2396
2397
                $this->getEntityPersister($class->name)->lock(
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2398
                    array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
2399
                    $lockMode
2400
                );
2401
                break;
2402
2403
            default:
2404
                // Do nothing
2405
        }
2406 2
    }
2407
2408
    /**
2409
     * Gets the CommitOrderCalculator used by the UnitOfWork to order commits.
2410
     *
2411
     * @return \Doctrine\ORM\Internal\CommitOrderCalculator
2412
     */
2413 1043
    public function getCommitOrderCalculator()
2414
    {
2415 1043
        return new Internal\CommitOrderCalculator();
2416
    }
2417
2418
    /**
2419
     * Clears the UnitOfWork.
2420
     *
2421
     * @param string|null $entityName if given, only entities of this type will get detached.
2422
     *
2423
     * @return void
2424
     *
2425
     * @throws ORMInvalidArgumentException if an invalid entity name is given
2426
     */
2427 1262
    public function clear($entityName = null)
2428
    {
2429 1262
        if ($entityName === null) {
2430 1260
            $this->identityMap =
2431 1260
            $this->entityIdentifiers =
2432 1260
            $this->originalEntityData =
2433 1260
            $this->entityChangeSets =
2434 1260
            $this->entityStates =
2435 1260
            $this->scheduledForSynchronization =
2436 1260
            $this->entityInsertions =
2437 1260
            $this->entityUpdates =
2438 1260
            $this->entityDeletions =
2439 1260
            $this->nonCascadedNewDetectedEntities =
2440 1260
            $this->collectionDeletions =
2441 1260
            $this->collectionUpdates =
2442 1260
            $this->extraUpdates =
2443 1260
            $this->readOnlyObjects =
2444 1260
            $this->visitedCollections =
2445 1260
            $this->orphanRemovals = [];
2446
        } else {
2447 4
            $this->clearIdentityMapForEntityName($entityName);
2448 4
            $this->clearEntityInsertionsForEntityName($entityName);
2449
        }
2450
2451 1262
        if ($this->evm->hasListeners(Events::onClear)) {
2452 9
            $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em, $entityName));
2453
        }
2454 1262
    }
2455
2456
    /**
2457
     * INTERNAL:
2458
     * Schedules an orphaned entity for removal. The remove() operation will be
2459
     * invoked on that entity at the beginning of the next commit of this
2460
     * UnitOfWork.
2461
     *
2462
     * @ignore
2463
     *
2464
     * @param object $entity
2465
     *
2466
     * @return void
2467
     */
2468 17
    public function scheduleOrphanRemoval($entity)
2469
    {
2470 17
        $this->orphanRemovals[spl_object_hash($entity)] = $entity;
2471 17
    }
2472
2473
    /**
2474
     * INTERNAL:
2475
     * Cancels a previously scheduled orphan removal.
2476
     *
2477
     * @ignore
2478
     *
2479
     * @param object $entity
2480
     *
2481
     * @return void
2482
     */
2483 117
    public function cancelOrphanRemoval($entity)
2484
    {
2485 117
        unset($this->orphanRemovals[spl_object_hash($entity)]);
2486 117
    }
2487
2488
    /**
2489
     * INTERNAL:
2490
     * Schedules a complete collection for removal when this UnitOfWork commits.
2491
     *
2492
     * @param PersistentCollection $coll
2493
     *
2494
     * @return void
2495
     */
2496 14
    public function scheduleCollectionDeletion(PersistentCollection $coll)
2497
    {
2498 14
        $coid = spl_object_hash($coll);
2499
2500
        // TODO: if $coll is already scheduled for recreation ... what to do?
2501
        // Just remove $coll from the scheduled recreations?
2502 14
        unset($this->collectionUpdates[$coid]);
2503
2504 14
        $this->collectionDeletions[$coid] = $coll;
2505 14
    }
2506
2507
    /**
2508
     * @param PersistentCollection $coll
2509
     *
2510
     * @return bool
2511
     */
2512
    public function isCollectionScheduledForDeletion(PersistentCollection $coll)
2513
    {
2514
        return isset($this->collectionDeletions[spl_object_hash($coll)]);
2515
    }
2516
2517
    /**
2518
     * @param ClassMetadata $class
2519
     *
2520
     * @return \Doctrine\Common\Persistence\ObjectManagerAware|object
2521
     */
2522 699
    private function newInstance($class)
2523
    {
2524 699
        $entity = $class->newInstance();
2525
2526 699
        if ($entity instanceof \Doctrine\Common\Persistence\ObjectManagerAware) {
2527 4
            $entity->injectObjectManager($this->em, $class);
2528
        }
2529
2530 699
        return $entity;
2531
    }
2532
2533
    /**
2534
     * INTERNAL:
2535
     * Creates an entity. Used for reconstitution of persistent entities.
2536
     *
2537
     * Internal note: Highly performance-sensitive method.
2538
     *
2539
     * @ignore
2540
     *
2541
     * @param string $className The name of the entity class.
2542
     * @param array  $data      The data for the entity.
2543
     * @param array  $hints     Any hints to account for during reconstitution/lookup of the entity.
2544
     *
2545
     * @return object The managed entity instance.
2546
     *
2547
     * @todo Rename: getOrCreateEntity
2548
     */
2549 841
    public function createEntity($className, array $data, &$hints = [])
2550
    {
2551 841
        $class = $this->em->getClassMetadata($className);
2552
        //$isReadOnly = isset($hints[Query::HINT_READ_ONLY]);
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2553
2554 841
        $id = $this->identifierFlattener->flattenIdentifier($class, $data);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
2555 841
        $idHash = implode(' ', $id);
2556
2557 841
        if (isset($this->identityMap[$class->rootEntityName][$idHash])) {
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
2558 323
            $entity = $this->identityMap[$class->rootEntityName][$idHash];
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
2559 323
            $oid = spl_object_hash($entity);
2560
2561
            if (
2562 323
                isset($hints[Query::HINT_REFRESH])
2563 323
                && isset($hints[Query::HINT_REFRESH_ENTITY])
2564 323
                && ($unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY]) !== $entity
2565 323
                && $unmanagedProxy instanceof Proxy
2566 323
                && $this->isIdentifierEquals($unmanagedProxy, $entity)
2567
            ) {
2568
                // DDC-1238 - we have a managed instance, but it isn't the provided one.
2569
                // Therefore we clear its identifier. Also, we must re-fetch metadata since the
2570
                // refreshed object may be anything
2571
2572 2
                foreach ($class->identifier as $fieldName) {
0 ignored issues
show
Bug introduced by
Accessing identifier 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...
2573 2
                    $class->reflFields[$fieldName]->setValue($unmanagedProxy, null);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
2574
                }
2575
2576 2
                return $unmanagedProxy;
2577
            }
2578
2579 321
            if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
2580 23
                $entity->__setInitialized(true);
2581
2582 23
                $overrideLocalValues = true;
2583
2584 23
                if ($entity instanceof NotifyPropertyChanged) {
2585 23
                    $entity->addPropertyChangedListener($this);
2586
                }
2587
            } else {
2588 300
                $overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
2589
2590
                // If only a specific entity is set to refresh, check that it's the one
2591 300
                if (isset($hints[Query::HINT_REFRESH_ENTITY])) {
2592 72
                    $overrideLocalValues = $hints[Query::HINT_REFRESH_ENTITY] === $entity;
2593
                }
2594
            }
2595
2596 321
            if ($overrideLocalValues) {
2597
                // inject ObjectManager upon refresh.
2598 115
                if ($entity instanceof ObjectManagerAware) {
2599 3
                    $entity->injectObjectManager($this->em, $class);
2600
                }
2601
2602 321
                $this->originalEntityData[$oid] = $data;
2603
            }
2604
        } else {
2605 694
            $entity = $this->newInstance($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\ORM\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...
2606 694
            $oid    = spl_object_hash($entity);
2607
2608 694
            $this->entityIdentifiers[$oid]  = $id;
2609 694
            $this->entityStates[$oid]       = self::STATE_MANAGED;
2610 694
            $this->originalEntityData[$oid] = $data;
2611
2612 694
            $this->identityMap[$class->rootEntityName][$idHash] = $entity;
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
2613
2614 694
            if ($entity instanceof NotifyPropertyChanged) {
2615 2
                $entity->addPropertyChangedListener($this);
2616
            }
2617
2618 694
            $overrideLocalValues = true;
2619
        }
2620
2621 840
        if ( ! $overrideLocalValues) {
2622 229
            return $entity;
2623
        }
2624
2625 732
        foreach ($data as $field => $value) {
2626 732
            if (isset($class->fieldMappings[$field])) {
0 ignored issues
show
Bug introduced by
Accessing fieldMappings 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...
2627 732
                $class->reflFields[$field]->setValue($entity, $value);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
2628
            }
2629
        }
2630
2631
        // Loading the entity right here, if its in the eager loading map get rid of it there.
2632 732
        unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
2633
2634 732
        if (isset($this->eagerLoadingEntities[$class->rootEntityName]) && ! $this->eagerLoadingEntities[$class->rootEntityName]) {
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
2635
            unset($this->eagerLoadingEntities[$class->rootEntityName]);
2636
        }
2637
2638
        // Properly initialize any unfetched associations, if partial objects are not allowed.
2639 732
        if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
2640 34
            return $entity;
2641
        }
2642
2643 698
        foreach ($class->associationMappings as $field => $assoc) {
0 ignored issues
show
Bug introduced by
Accessing associationMappings 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...
2644
            // Check if the association is not among the fetch-joined associations already.
2645 602
            if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) {
2646 258
                continue;
2647
            }
2648
2649 578
            $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
2650
2651
            switch (true) {
2652 578
                case ($assoc['type'] & ClassMetadata::TO_ONE):
2653 498
                    if ( ! $assoc['isOwningSide']) {
2654
2655
                        // use the given entity association
2656 67
                        if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2657
2658 3
                            $this->originalEntityData[$oid][$field] = $data[$field];
2659
2660 3
                            $class->reflFields[$field]->setValue($entity, $data[$field]);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
2661 3
                            $targetClass->reflFields[$assoc['mappedBy']]->setValue($data[$field], $entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
2662
2663 3
                            continue 2;
2664
                        }
2665
2666
                        // Inverse side of x-to-one can never be lazy
2667 64
                        $class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity));
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
2668
2669 64
                        continue 2;
2670
                    }
2671
2672
                    // use the entity association
2673 498
                    if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2674 38
                        $class->reflFields[$field]->setValue($entity, $data[$field]);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
2675 38
                        $this->originalEntityData[$oid][$field] = $data[$field];
2676
2677 38
                        continue;
2678
                    }
2679
2680 491
                    $associatedId = [];
2681
2682
                    // TODO: Is this even computed right in all cases of composite keys?
2683 491
                    foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
2684 491
                        $joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
2685
2686 491
                        if ($joinColumnValue !== null) {
2687 295
                            if ($targetClass->containsForeignIdentifier) {
0 ignored issues
show
Bug introduced by
Accessing containsForeignIdentifier 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...
2688 11
                                $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
2689
                            } else {
2690 295
                                $associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
0 ignored issues
show
Bug introduced by
Accessing fieldNames 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...
2691
                            }
2692 289
                        } elseif ($targetClass->containsForeignIdentifier
0 ignored issues
show
Bug introduced by
Accessing containsForeignIdentifier 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...
2693 289
                            && in_array($targetClass->getFieldForColumn($targetColumn), $targetClass->identifier, true)
0 ignored issues
show
Bug introduced by
Accessing identifier 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...
2694
                        ) {
2695
                            // the missing key is part of target's entity primary key
2696 7
                            $associatedId = [];
2697 491
                            break;
2698
                        }
2699
                    }
2700
2701 491
                    if ( ! $associatedId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $associatedId of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2702
                        // Foreign key is NULL
2703 289
                        $class->reflFields[$field]->setValue($entity, null);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
2704 289
                        $this->originalEntityData[$oid][$field] = null;
2705
2706 289
                        continue;
2707
                    }
2708
2709 295
                    if ( ! isset($hints['fetchMode'][$class->name][$field])) {
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2710 292
                        $hints['fetchMode'][$class->name][$field] = $assoc['fetch'];
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2711
                    }
2712
2713
                    // Foreign key is set
2714
                    // Check identity map first
2715
                    // FIXME: Can break easily with composite keys if join column values are in
2716
                    //        wrong order. The correct order is the one in ClassMetadata#identifier.
2717 295
                    $relatedIdHash = implode(' ', $associatedId);
2718
2719
                    switch (true) {
2720 295
                        case (isset($this->identityMap[$targetClass->rootEntityName][$relatedIdHash])):
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
2721 170
                            $newValue = $this->identityMap[$targetClass->rootEntityName][$relatedIdHash];
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
2722
2723
                            // If this is an uninitialized proxy, we are deferring eager loads,
2724
                            // this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
2725
                            // then we can append this entity for eager loading!
2726 170
                            if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER &&
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2727 170
                                isset($hints[self::HINT_DEFEREAGERLOAD]) &&
2728 170
                                !$targetClass->isIdentifierComposite &&
0 ignored issues
show
Bug introduced by
Accessing isIdentifierComposite 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...
2729 170
                                $newValue instanceof Proxy &&
2730 170
                                $newValue->__isInitialized__ === false) {
0 ignored issues
show
Bug introduced by
Accessing __isInitialized__ on the interface Doctrine\ORM\Proxy\Proxy 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...
2731
2732
                                $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
2733
                            }
2734
2735 170
                            break;
2736
2737 201
                        case ($targetClass->subClasses):
0 ignored issues
show
Bug introduced by
Accessing subClasses 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...
2738
                            // If it might be a subtype, it can not be lazy. There isn't even
2739
                            // a way to solve this with deferred eager loading, which means putting
2740
                            // an entity with subclasses at a *-to-one location is really bad! (performance-wise)
2741 32
                            $newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, $associatedId);
2742 32
                            break;
2743
2744
                        default:
2745
                            switch (true) {
2746
                                // We are negating the condition here. Other cases will assume it is valid!
2747 171
                                case ($hints['fetchMode'][$class->name][$field] !== ClassMetadata::FETCH_EAGER):
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2748 164
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2749 164
                                    break;
2750
2751
                                // Deferred eager load only works for single identifier classes
2752 7
                                case (isset($hints[self::HINT_DEFEREAGERLOAD]) && ! $targetClass->isIdentifierComposite):
0 ignored issues
show
Bug introduced by
Accessing isIdentifierComposite 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...
2753
                                    // TODO: Is there a faster approach?
2754 7
                                    $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
2755
2756 7
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2757 7
                                    break;
2758
2759
                                default:
2760
                                    // TODO: This is very imperformant, ignore it?
2761
                                    $newValue = $this->em->find($assoc['targetEntity'], $associatedId);
2762
                                    break;
2763
                            }
2764
2765
                            // PERF: Inlined & optimized code from UnitOfWork#registerManaged()
2766 171
                            $newValueOid = spl_object_hash($newValue);
2767 171
                            $this->entityIdentifiers[$newValueOid] = $associatedId;
2768 171
                            $this->identityMap[$targetClass->rootEntityName][$relatedIdHash] = $newValue;
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
2769
2770
                            if (
2771 171
                                $newValue instanceof NotifyPropertyChanged &&
2772 171
                                ( ! $newValue instanceof Proxy || $newValue->__isInitialized())
2773
                            ) {
2774
                                $newValue->addPropertyChangedListener($this);
2775
                            }
2776 171
                            $this->entityStates[$newValueOid] = self::STATE_MANAGED;
2777
                            // make sure that when an proxy is then finally loaded, $this->originalEntityData is set also!
2778 171
                            break;
2779
                    }
2780
2781 295
                    $this->originalEntityData[$oid][$field] = $newValue;
2782 295
                    $class->reflFields[$field]->setValue($entity, $newValue);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
2783
2784 295
                    if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
2785 57
                        $inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']];
0 ignored issues
show
Bug introduced by
Accessing associationMappings 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...
2786 57
                        $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($newValue, $entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
2787
                    }
2788
2789 295
                    break;
2790
2791
                default:
2792
                    // Ignore if its a cached collection
2793 492
                    if (isset($hints[Query::HINT_CACHE_ENABLED]) && $class->getFieldValue($entity, $field) instanceof PersistentCollection) {
2794
                        break;
2795
                    }
2796
2797
                    // use the given collection
2798 492
                    if (isset($data[$field]) && $data[$field] instanceof PersistentCollection) {
2799
2800 3
                        $data[$field]->setOwner($entity, $assoc);
2801
2802 3
                        $class->reflFields[$field]->setValue($entity, $data[$field]);
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
2803 3
                        $this->originalEntityData[$oid][$field] = $data[$field];
2804
2805 3
                        break;
2806
                    }
2807
2808
                    // Inject collection
2809 492
                    $pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection);
0 ignored issues
show
Compatibility introduced by
$targetClass of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
2810 492
                    $pColl->setOwner($entity, $assoc);
2811 492
                    $pColl->setInitialized(false);
2812
2813 492
                    $reflField = $class->reflFields[$field];
0 ignored issues
show
Bug introduced by
Accessing reflFields 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...
2814 492
                    $reflField->setValue($entity, $pColl);
2815
2816 492
                    if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
2817 4
                        $this->loadCollection($pColl);
2818 4
                        $pColl->takeSnapshot();
2819
                    }
2820
2821 492
                    $this->originalEntityData[$oid][$field] = $pColl;
2822 578
                    break;
2823
            }
2824
        }
2825
2826 698
        if ($overrideLocalValues) {
2827
            // defer invoking of postLoad event to hydration complete step
2828 698
            $this->hydrationCompleteHandler->deferPostLoadInvoking($class, $entity);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
2829
        }
2830
2831 698
        return $entity;
2832
    }
2833
2834
    /**
2835
     * @return void
2836
     */
2837 906
    public function triggerEagerLoads()
2838
    {
2839 906
        if ( ! $this->eagerLoadingEntities) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->eagerLoadingEntities of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2840 906
            return;
2841
        }
2842
2843
        // avoid infinite recursion
2844 7
        $eagerLoadingEntities       = $this->eagerLoadingEntities;
2845 7
        $this->eagerLoadingEntities = [];
2846
2847 7
        foreach ($eagerLoadingEntities as $entityName => $ids) {
2848 7
            if ( ! $ids) {
2849
                continue;
2850
            }
2851
2852 7
            $class = $this->em->getClassMetadata($entityName);
2853
2854 7
            $this->getEntityPersister($entityName)->loadAll(
2855 7
                array_combine($class->identifier, [array_values($ids)])
0 ignored issues
show
Bug introduced by
Accessing identifier 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...
2856
            );
2857
        }
2858 7
    }
2859
2860
    /**
2861
     * Initializes (loads) an uninitialized persistent collection of an entity.
2862
     *
2863
     * @param \Doctrine\ORM\PersistentCollection $collection The collection to initialize.
2864
     *
2865
     * @return void
2866
     *
2867
     * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733.
2868
     */
2869 147
    public function loadCollection(PersistentCollection $collection)
2870
    {
2871 147
        $assoc     = $collection->getMapping();
2872 147
        $persister = $this->getEntityPersister($assoc['targetEntity']);
2873
2874 147
        switch ($assoc['type']) {
2875 147
            case ClassMetadata::ONE_TO_MANY:
2876 77
                $persister->loadOneToManyCollection($assoc, $collection->getOwner(), $collection);
2877 77
                break;
2878
2879 84
            case ClassMetadata::MANY_TO_MANY:
2880 84
                $persister->loadManyToManyCollection($assoc, $collection->getOwner(), $collection);
2881 84
                break;
2882
        }
2883
2884 147
        $collection->setInitialized(true);
2885 147
    }
2886
2887
    /**
2888
     * Gets the identity map of the UnitOfWork.
2889
     *
2890
     * @return array
2891
     */
2892 2
    public function getIdentityMap()
2893
    {
2894 2
        return $this->identityMap;
2895
    }
2896
2897
    /**
2898
     * Gets the original data of an entity. The original data is the data that was
2899
     * present at the time the entity was reconstituted from the database.
2900
     *
2901
     * @param object $entity
2902
     *
2903
     * @return array
2904
     */
2905 120
    public function getOriginalEntityData($entity)
2906
    {
2907 120
        $oid = spl_object_hash($entity);
2908
2909 120
        return isset($this->originalEntityData[$oid])
2910 116
            ? $this->originalEntityData[$oid]
2911 120
            : [];
2912
    }
2913
2914
    /**
2915
     * @ignore
2916
     *
2917
     * @param object $entity
2918
     * @param array  $data
2919
     *
2920
     * @return void
2921
     */
2922
    public function setOriginalEntityData($entity, array $data)
2923
    {
2924
        $this->originalEntityData[spl_object_hash($entity)] = $data;
2925
    }
2926
2927
    /**
2928
     * INTERNAL:
2929
     * Sets a property value of the original data array of an entity.
2930
     *
2931
     * @ignore
2932
     *
2933
     * @param string $oid
2934
     * @param string $property
2935
     * @param mixed  $value
2936
     *
2937
     * @return void
2938
     */
2939 312
    public function setOriginalEntityProperty($oid, $property, $value)
2940
    {
2941 312
        $this->originalEntityData[$oid][$property] = $value;
2942 312
    }
2943
2944
    /**
2945
     * Gets the identifier of an entity.
2946
     * The returned value is always an array of identifier values. If the entity
2947
     * has a composite identifier then the identifier values are in the same
2948
     * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames().
2949
     *
2950
     * @param object $entity
2951
     *
2952
     * @return array The identifier values.
2953
     */
2954 860
    public function getEntityIdentifier($entity)
2955
    {
2956 860
        return $this->entityIdentifiers[spl_object_hash($entity)];
2957
    }
2958
2959
    /**
2960
     * Processes an entity instance to extract their identifier values.
2961
     *
2962
     * @param object $entity The entity instance.
2963
     *
2964
     * @return mixed A scalar value.
2965
     *
2966
     * @throws \Doctrine\ORM\ORMInvalidArgumentException
2967
     */
2968 127
    public function getSingleIdentifierValue($entity)
2969
    {
2970 127
        $class = $this->em->getClassMetadata(get_class($entity));
2971
2972 127
        if ($class->isIdentifierComposite) {
0 ignored issues
show
Bug introduced by
Accessing isIdentifierComposite 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...
2973
            throw ORMInvalidArgumentException::invalidCompositeIdentifier();
2974
        }
2975
2976 127
        $values = $this->isInIdentityMap($entity)
2977 114
            ? $this->getEntityIdentifier($entity)
2978 127
            : $class->getIdentifierValues($entity);
2979
2980 127
        return isset($values[$class->identifier[0]]) ? $values[$class->identifier[0]] : null;
0 ignored issues
show
Bug introduced by
Accessing identifier 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...
2981
    }
2982
2983
    /**
2984
     * Tries to find an entity with the given identifier in the identity map of
2985
     * this UnitOfWork.
2986
     *
2987
     * @param mixed  $id            The entity identifier to look for.
2988
     * @param string $rootClassName The name of the root class of the mapped entity hierarchy.
2989
     *
2990
     * @return object|bool Returns the entity with the specified identifier if it exists in
2991
     *                     this UnitOfWork, FALSE otherwise.
2992
     */
2993 545 View Code Duplication
    public function tryGetById($id, $rootClassName)
2994
    {
2995 545
        $idHash = implode(' ', (array) $id);
2996
2997 545
        return isset($this->identityMap[$rootClassName][$idHash])
2998 85
            ? $this->identityMap[$rootClassName][$idHash]
2999 545
            : false;
3000
    }
3001
3002
    /**
3003
     * Schedules an entity for dirty-checking at commit-time.
3004
     *
3005
     * @param object $entity The entity to schedule for dirty-checking.
3006
     *
3007
     * @return void
3008
     *
3009
     * @todo Rename: scheduleForSynchronization
3010
     */
3011 5
    public function scheduleForDirtyCheck($entity)
3012
    {
3013 5
        $rootClassName = $this->em->getClassMetadata(get_class($entity))->rootEntityName;
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
3014
3015 5
        $this->scheduledForSynchronization[$rootClassName][spl_object_hash($entity)] = $entity;
3016 5
    }
3017
3018
    /**
3019
     * Checks whether the UnitOfWork has any pending insertions.
3020
     *
3021
     * @return boolean TRUE if this UnitOfWork has pending insertions, FALSE otherwise.
3022
     */
3023
    public function hasPendingInsertions()
3024
    {
3025
        return ! empty($this->entityInsertions);
3026
    }
3027
3028
    /**
3029
     * Calculates the size of the UnitOfWork. The size of the UnitOfWork is the
3030
     * number of entities in the identity map.
3031
     *
3032
     * @return integer
3033
     */
3034 1
    public function size()
3035
    {
3036 1
        $countArray = array_map('count', $this->identityMap);
3037
3038 1
        return array_sum($countArray);
3039
    }
3040
3041
    /**
3042
     * Gets the EntityPersister for an Entity.
3043
     *
3044
     * @param string $entityName The name of the Entity.
3045
     *
3046
     * @return \Doctrine\ORM\Persisters\Entity\EntityPersister
3047
     */
3048 1108
    public function getEntityPersister($entityName)
3049
    {
3050 1108
        if (isset($this->persisters[$entityName])) {
3051 870
            return $this->persisters[$entityName];
3052
        }
3053
3054 1108
        $class = $this->em->getClassMetadata($entityName);
3055
3056
        switch (true) {
3057 1108
            case ($class->isInheritanceTypeNone()):
3058 1062
                $persister = new BasicEntityPersister($this->em, $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\ORM\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...
3059 1062
                break;
3060
3061 379
            case ($class->isInheritanceTypeSingleTable()):
3062 225
                $persister = new SingleTablePersister($this->em, $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\ORM\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...
3063 225
                break;
3064
3065 347
            case ($class->isInheritanceTypeJoined()):
3066 347
                $persister = new JoinedSubclassPersister($this->em, $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\ORM\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...
3067 347
                break;
3068
3069
            default:
3070
                throw new \RuntimeException('No persister found for entity.');
3071
        }
3072
3073 1108
        if ($this->hasCache && $class->cache !== null) {
0 ignored issues
show
Bug introduced by
Accessing cache 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...
3074 124
            $persister = $this->em->getConfiguration()
3075 124
                ->getSecondLevelCacheConfiguration()
3076 124
                ->getCacheFactory()
3077 124
                ->buildCachedEntityPersister($this->em, $persister, $class);
3078
        }
3079
3080 1108
        $this->persisters[$entityName] = $persister;
3081
3082 1108
        return $this->persisters[$entityName];
3083
    }
3084
3085
    /**
3086
     * Gets a collection persister for a collection-valued association.
3087
     *
3088
     * @param array $association
3089
     *
3090
     * @return \Doctrine\ORM\Persisters\Collection\CollectionPersister
3091
     */
3092 574
    public function getCollectionPersister(array $association)
3093
    {
3094 574
        $role = isset($association['cache'])
3095 78
            ? $association['sourceEntity'] . '::' . $association['fieldName']
3096 574
            : $association['type'];
3097
3098 574
        if (isset($this->collectionPersisters[$role])) {
3099 452
            return $this->collectionPersisters[$role];
3100
        }
3101
3102 574
        $persister = ClassMetadata::ONE_TO_MANY === $association['type']
3103 408
            ? new OneToManyPersister($this->em)
3104 574
            : new ManyToManyPersister($this->em);
3105
3106 574
        if ($this->hasCache && isset($association['cache'])) {
3107 77
            $persister = $this->em->getConfiguration()
3108 77
                ->getSecondLevelCacheConfiguration()
3109 77
                ->getCacheFactory()
3110 77
                ->buildCachedCollectionPersister($this->em, $persister, $association);
3111
        }
3112
3113 574
        $this->collectionPersisters[$role] = $persister;
3114
3115 574
        return $this->collectionPersisters[$role];
3116
    }
3117
3118
    /**
3119
     * INTERNAL:
3120
     * Registers an entity as managed.
3121
     *
3122
     * @param object $entity The entity.
3123
     * @param array  $id     The identifier values.
3124
     * @param array  $data   The original entity data.
3125
     *
3126
     * @return void
3127
     */
3128 209
    public function registerManaged($entity, array $id, array $data)
3129
    {
3130 209
        $oid = spl_object_hash($entity);
3131
3132 209
        $this->entityIdentifiers[$oid]  = $id;
3133 209
        $this->entityStates[$oid]       = self::STATE_MANAGED;
3134 209
        $this->originalEntityData[$oid] = $data;
3135
3136 209
        $this->addToIdentityMap($entity);
3137
3138 203
        if ($entity instanceof NotifyPropertyChanged && ( ! $entity instanceof Proxy || $entity->__isInitialized())) {
3139 2
            $entity->addPropertyChangedListener($this);
3140
        }
3141 203
    }
3142
3143
    /**
3144
     * INTERNAL:
3145
     * Clears the property changeset of the entity with the given OID.
3146
     *
3147
     * @param string $oid The entity's OID.
3148
     *
3149
     * @return void
3150
     */
3151 15
    public function clearEntityChangeSet($oid)
3152
    {
3153 15
        unset($this->entityChangeSets[$oid]);
3154 15
    }
3155
3156
    /* PropertyChangedListener implementation */
3157
3158
    /**
3159
     * Notifies this UnitOfWork of a property change in an entity.
3160
     *
3161
     * @param object $entity       The entity that owns the property.
3162
     * @param string $propertyName The name of the property that changed.
3163
     * @param mixed  $oldValue     The old value of the property.
3164
     * @param mixed  $newValue     The new value of the property.
3165
     *
3166
     * @return void
3167
     */
3168 3
    public function propertyChanged($entity, $propertyName, $oldValue, $newValue)
3169
    {
3170 3
        $oid   = spl_object_hash($entity);
3171 3
        $class = $this->em->getClassMetadata(get_class($entity));
3172
3173 3
        $isAssocField = isset($class->associationMappings[$propertyName]);
0 ignored issues
show
Bug introduced by
Accessing associationMappings 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...
3174
3175 3
        if ( ! $isAssocField && ! isset($class->fieldMappings[$propertyName])) {
0 ignored issues
show
Bug introduced by
Accessing fieldMappings 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...
3176 1
            return; // ignore non-persistent fields
3177
        }
3178
3179
        // Update changeset and mark entity for synchronization
3180 3
        $this->entityChangeSets[$oid][$propertyName] = [$oldValue, $newValue];
3181
3182 3
        if ( ! isset($this->scheduledForSynchronization[$class->rootEntityName][$oid])) {
0 ignored issues
show
Bug introduced by
Accessing rootEntityName 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...
3183 3
            $this->scheduleForDirtyCheck($entity);
3184
        }
3185 3
    }
3186
3187
    /**
3188
     * Gets the currently scheduled entity insertions in this UnitOfWork.
3189
     *
3190
     * @return array
3191
     */
3192 2
    public function getScheduledEntityInsertions()
3193
    {
3194 2
        return $this->entityInsertions;
3195
    }
3196
3197
    /**
3198
     * Gets the currently scheduled entity updates in this UnitOfWork.
3199
     *
3200
     * @return array
3201
     */
3202 3
    public function getScheduledEntityUpdates()
3203
    {
3204 3
        return $this->entityUpdates;
3205
    }
3206
3207
    /**
3208
     * Gets the currently scheduled entity deletions in this UnitOfWork.
3209
     *
3210
     * @return array
3211
     */
3212 1
    public function getScheduledEntityDeletions()
3213
    {
3214 1
        return $this->entityDeletions;
3215
    }
3216
3217
    /**
3218
     * Gets the currently scheduled complete collection deletions
3219
     *
3220
     * @return array
3221
     */
3222 1
    public function getScheduledCollectionDeletions()
3223
    {
3224 1
        return $this->collectionDeletions;
3225
    }
3226
3227
    /**
3228
     * Gets the currently scheduled collection inserts, updates and deletes.
3229
     *
3230
     * @return array
3231
     */
3232
    public function getScheduledCollectionUpdates()
3233
    {
3234
        return $this->collectionUpdates;
3235
    }
3236
3237
    /**
3238
     * Helper method to initialize a lazy loading proxy or persistent collection.
3239
     *
3240
     * @param object $obj
3241
     *
3242
     * @return void
3243
     */
3244 2
    public function initializeObject($obj)
3245
    {
3246 2
        if ($obj instanceof Proxy) {
3247 1
            $obj->__load();
3248
3249 1
            return;
3250
        }
3251
3252 1
        if ($obj instanceof PersistentCollection) {
3253 1
            $obj->initialize();
3254
        }
3255 1
    }
3256
3257
    /**
3258
     * Helper method to show an object as string.
3259
     *
3260
     * @param object $obj
3261
     *
3262
     * @return string
3263
     */
3264 1
    private static function objToStr($obj)
3265
    {
3266 1
        return method_exists($obj, '__toString') ? (string) $obj : get_class($obj).'@'.spl_object_hash($obj);
3267
    }
3268
3269
    /**
3270
     * Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit().
3271
     *
3272
     * This operation cannot be undone as some parts of the UnitOfWork now keep gathering information
3273
     * on this object that might be necessary to perform a correct update.
3274
     *
3275
     * @param object $object
3276
     *
3277
     * @return void
3278
     *
3279
     * @throws ORMInvalidArgumentException
3280
     */
3281 6
    public function markReadOnly($object)
3282
    {
3283 6
        if ( ! is_object($object) || ! $this->isInIdentityMap($object)) {
3284 1
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3285
        }
3286
3287 5
        $this->readOnlyObjects[spl_object_hash($object)] = true;
3288 5
    }
3289
3290
    /**
3291
     * Is this entity read only?
3292
     *
3293
     * @param object $object
3294
     *
3295
     * @return bool
3296
     *
3297
     * @throws ORMInvalidArgumentException
3298
     */
3299 3
    public function isReadOnly($object)
3300
    {
3301 3
        if ( ! is_object($object)) {
3302
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3303
        }
3304
3305 3
        return isset($this->readOnlyObjects[spl_object_hash($object)]);
3306
    }
3307
3308
    /**
3309
     * Perform whatever processing is encapsulated here after completion of the transaction.
3310
     */
3311
    private function afterTransactionComplete()
3312
    {
3313 1038
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3314 93
            $persister->afterTransactionComplete();
3315 1038
        });
3316 1038
    }
3317
3318
    /**
3319
     * Perform whatever processing is encapsulated here after completion of the rolled-back.
3320
     */
3321
    private function afterTransactionRolledBack()
3322
    {
3323 11
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3324 3
            $persister->afterTransactionRolledBack();
3325 11
        });
3326 11
    }
3327
3328
    /**
3329
     * Performs an action after the transaction.
3330
     *
3331
     * @param callable $callback
3332
     */
3333 1043
    private function performCallbackOnCachedPersister(callable $callback)
3334
    {
3335 1043
        if ( ! $this->hasCache) {
3336 950
            return;
3337
        }
3338
3339 93
        foreach (array_merge($this->persisters, $this->collectionPersisters) as $persister) {
3340 93
            if ($persister instanceof CachedPersister) {
3341 93
                $callback($persister);
3342
            }
3343
        }
3344 93
    }
3345
3346 1047
    private function dispatchOnFlushEvent()
3347
    {
3348 1047
        if ($this->evm->hasListeners(Events::onFlush)) {
3349 4
            $this->evm->dispatchEvent(Events::onFlush, new OnFlushEventArgs($this->em));
3350
        }
3351 1047
    }
3352
3353 1042
    private function dispatchPostFlushEvent()
3354
    {
3355 1042
        if ($this->evm->hasListeners(Events::postFlush)) {
3356 5
            $this->evm->dispatchEvent(Events::postFlush, new PostFlushEventArgs($this->em));
3357
        }
3358 1041
    }
3359
3360
    /**
3361
     * Verifies if two given entities actually are the same based on identifier comparison
3362
     *
3363
     * @param object $entity1
3364
     * @param object $entity2
3365
     *
3366
     * @return bool
3367
     */
3368 14
    private function isIdentifierEquals($entity1, $entity2)
3369
    {
3370 14
        if ($entity1 === $entity2) {
3371
            return true;
3372
        }
3373
3374 14
        $class = $this->em->getClassMetadata(get_class($entity1));
3375
3376 14
        if ($class !== $this->em->getClassMetadata(get_class($entity2))) {
3377 11
            return false;
3378
        }
3379
3380 3
        $oid1 = spl_object_hash($entity1);
3381 3
        $oid2 = spl_object_hash($entity2);
3382
3383 3
        $id1 = isset($this->entityIdentifiers[$oid1])
3384 3
            ? $this->entityIdentifiers[$oid1]
3385 3
            : $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($entity1));
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
3386 3
        $id2 = isset($this->entityIdentifiers[$oid2])
3387 3
            ? $this->entityIdentifiers[$oid2]
3388 3
            : $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($entity2));
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
3389
3390 3
        return $id1 === $id2 || implode(' ', $id1) === implode(' ', $id2);
3391
    }
3392
3393
    /**
3394
     * @throws ORMInvalidArgumentException
3395
     */
3396 1045
    private function assertThatThereAreNoUnintentionallyNonPersistedAssociations() : void
3397
    {
3398 1045
        $entitiesNeedingCascadePersist = \array_diff_key($this->nonCascadedNewDetectedEntities, $this->entityInsertions);
3399
3400 1045
        $this->nonCascadedNewDetectedEntities = [];
3401
3402 1045
        if ($entitiesNeedingCascadePersist) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $entitiesNeedingCascadePersist of type array<object|array>[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
3403 5
            throw ORMInvalidArgumentException::newEntitiesFoundThroughRelationships(
3404 5
                \array_values($entitiesNeedingCascadePersist)
3405
            );
3406
        }
3407 1043
    }
3408
3409
    /**
3410
     * @param object $entity
3411
     * @param object $managedCopy
3412
     *
3413
     * @throws ORMException
3414
     * @throws OptimisticLockException
3415
     * @throws TransactionRequiredException
3416
     */
3417 40
    private function mergeEntityStateIntoManagedCopy($entity, $managedCopy)
3418
    {
3419 40
        if (! $this->isLoaded($entity)) {
3420 7
            return;
3421
        }
3422
3423 33
        if (! $this->isLoaded($managedCopy)) {
3424 4
            $managedCopy->__load();
3425
        }
3426
3427 33
        $class = $this->em->getClassMetadata(get_class($entity));
3428
3429 33
        foreach ($this->reflectionPropertiesGetter->getProperties($class->name) as $prop) {
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
3430 33
            $name = $prop->name;
3431
3432 33
            $prop->setAccessible(true);
3433
3434 33
            if ( ! isset($class->associationMappings[$name])) {
0 ignored issues
show
Bug introduced by
Accessing associationMappings 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...
3435 33
                if ( ! $class->isIdentifier($name)) {
3436 33
                    $prop->setValue($managedCopy, $prop->getValue($entity));
3437
                }
3438
            } else {
3439 29
                $assoc2 = $class->associationMappings[$name];
0 ignored issues
show
Bug introduced by
Accessing associationMappings 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...
3440
3441 29
                if ($assoc2['type'] & ClassMetadata::TO_ONE) {
3442 25
                    $other = $prop->getValue($entity);
3443 25
                    if ($other === null) {
3444 12
                        $prop->setValue($managedCopy, null);
3445
                    } else {
3446 16
                        if ($other instanceof Proxy && !$other->__isInitialized()) {
3447
                            // do not merge fields marked lazy that have not been fetched.
3448 4
                            continue;
3449
                        }
3450
3451 12
                        if ( ! $assoc2['isCascadeMerge']) {
3452 6
                            if ($this->getEntityState($other) === self::STATE_DETACHED) {
3453 3
                                $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']);
3454 3
                                $relatedId   = $targetClass->getIdentifierValues($other);
3455
3456 3
                                if ($targetClass->subClasses) {
0 ignored issues
show
Bug introduced by
Accessing subClasses 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...
3457 2
                                    $other = $this->em->find($targetClass->name, $relatedId);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
3458
                                } else {
3459 1
                                    $other = $this->em->getProxyFactory()->getProxy(
3460 1
                                        $assoc2['targetEntity'],
3461 1
                                        $relatedId
3462
                                    );
3463 1
                                    $this->registerManaged($other, $relatedId, []);
3464
                                }
3465
                            }
3466
3467 21
                            $prop->setValue($managedCopy, $other);
3468
                        }
3469
                    }
3470
                } else {
3471 17
                    $mergeCol = $prop->getValue($entity);
3472
3473 17
                    if ($mergeCol instanceof PersistentCollection && ! $mergeCol->isInitialized()) {
3474
                        // do not merge fields marked lazy that have not been fetched.
3475
                        // keep the lazy persistent collection of the managed copy.
3476 5
                        continue;
3477
                    }
3478
3479 14
                    $managedCol = $prop->getValue($managedCopy);
3480
3481 14
                    if ( ! $managedCol) {
3482 4
                        $managedCol = new PersistentCollection(
3483 4
                            $this->em,
3484 4
                            $this->em->getClassMetadata($assoc2['targetEntity']),
0 ignored issues
show
Compatibility introduced by
$this->em->getClassMetad...assoc2['targetEntity']) of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\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...
3485 4
                            new ArrayCollection
3486
                        );
3487 4
                        $managedCol->setOwner($managedCopy, $assoc2);
3488 4
                        $prop->setValue($managedCopy, $managedCol);
3489
                    }
3490
3491 14
                    if ($assoc2['isCascadeMerge']) {
3492 9
                        $managedCol->initialize();
3493
3494
                        // clear and set dirty a managed collection if its not also the same collection to merge from.
3495 9
                        if ( ! $managedCol->isEmpty() && $managedCol !== $mergeCol) {
3496 1
                            $managedCol->unwrap()->clear();
3497 1
                            $managedCol->setDirty(true);
3498
3499 1
                            if ($assoc2['isOwningSide']
3500 1
                                && $assoc2['type'] == ClassMetadata::MANY_TO_MANY
3501 1
                                && $class->isChangeTrackingNotify()
3502
                            ) {
3503
                                $this->scheduleForDirtyCheck($managedCopy);
3504
                            }
3505
                        }
3506
                    }
3507
                }
3508
            }
3509
3510 33
            if ($class->isChangeTrackingNotify()) {
3511
                // Just treat all properties as changed, there is no other choice.
3512 33
                $this->propertyChanged($managedCopy, $name, null, $prop->getValue($managedCopy));
3513
            }
3514
        }
3515 33
    }
3516
3517
    /**
3518
     * This method called by hydrators, and indicates that hydrator totally completed current hydration cycle.
3519
     * Unit of work able to fire deferred events, related to loading events here.
3520
     *
3521
     * @internal should be called internally from object hydrators
3522
     */
3523 920
    public function hydrationComplete()
3524
    {
3525 920
        $this->hydrationCompleteHandler->hydrationComplete();
3526 920
    }
3527
3528
    /**
3529
     * @param string $entityName
3530
     */
3531 4
    private function clearIdentityMapForEntityName($entityName)
3532
    {
3533 4
        if (! isset($this->identityMap[$entityName])) {
3534
            return;
3535
        }
3536
3537 4
        $visited = [];
3538
3539 4
        foreach ($this->identityMap[$entityName] as $entity) {
3540 4
            $this->doDetach($entity, $visited, false);
3541
        }
3542 4
    }
3543
3544
    /**
3545
     * @param string $entityName
3546
     */
3547 4
    private function clearEntityInsertionsForEntityName($entityName)
3548
    {
3549 4
        foreach ($this->entityInsertions as $hash => $entity) {
3550
            // note: performance optimization - `instanceof` is much faster than a function call
3551 1
            if ($entity instanceof $entityName && get_class($entity) === $entityName) {
3552 1
                unset($this->entityInsertions[$hash]);
3553
            }
3554
        }
3555 4
    }
3556
3557
    /**
3558
     * @param ClassMetadata $class
3559
     * @param mixed         $identifierValue
3560
     *
3561
     * @return mixed the identifier after type conversion
3562
     *
3563
     * @throws \Doctrine\ORM\Mapping\MappingException if the entity has more than a single identifier
3564
     */
3565 945
    private function convertSingleFieldIdentifierToPHPValue(ClassMetadata $class, $identifierValue)
3566
    {
3567 945
        return $this->em->getConnection()->convertToPHPValue(
3568 945
            $identifierValue,
3569 945
            $class->getTypeOfField($class->getSingleIdentifierFieldName())
0 ignored issues
show
Bug introduced by
It seems like $class->getTypeOfField($...eIdentifierFieldName()) targeting Doctrine\ORM\Mapping\Cla...aInfo::getTypeOfField() can also be of type null or object<Doctrine\DBAL\Types\Type>; however, Doctrine\DBAL\Connection::convertToPHPValue() does only seem to accept string, 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...
3570
        );
3571
    }
3572
}
3573