Failed Conditions
Push — master ( 577045...60e29b )
by Marco
19:50
created

UnitOfWork::computeChangeSets()   C

Complexity

Conditions 11
Paths 14

Size

Total Lines 45
Code Lines 21

Duplication

Lines 3
Ratio 6.67 %

Code Coverage

Tests 20
CRAP Score 11

Importance

Changes 0
Metric Value
dl 3
loc 45
ccs 20
cts 20
cp 1
rs 5.2653
c 0
b 0
f 0
cc 11
eloc 21
nc 14
nop 0
crap 11

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
     * All pending collection deletions.
183
     *
184
     * @var array
185
     */
186
    private $collectionDeletions = [];
187
188
    /**
189
     * All pending collection updates.
190
     *
191
     * @var array
192
     */
193
    private $collectionUpdates = [];
194
195
    /**
196
     * List of collections visited during changeset calculation on a commit-phase of a UnitOfWork.
197
     * At the end of the UnitOfWork all these collections will make new snapshots
198
     * of their data.
199
     *
200
     * @var array
201
     */
202
    private $visitedCollections = [];
203
204
    /**
205
     * The EntityManager that "owns" this UnitOfWork instance.
206
     *
207
     * @var EntityManagerInterface
208
     */
209
    private $em;
210
211
    /**
212
     * The entity persister instances used to persist entity instances.
213
     *
214
     * @var array
215
     */
216
    private $persisters = [];
217
218
    /**
219
     * The collection persister instances used to persist collections.
220
     *
221
     * @var array
222
     */
223
    private $collectionPersisters = [];
224
225
    /**
226
     * The EventManager used for dispatching events.
227
     *
228
     * @var \Doctrine\Common\EventManager
229
     */
230
    private $evm;
231
232
    /**
233
     * The ListenersInvoker used for dispatching events.
234
     *
235
     * @var \Doctrine\ORM\Event\ListenersInvoker
236
     */
237
    private $listenersInvoker;
238
239
    /**
240
     * The IdentifierFlattener used for manipulating identifiers
241
     *
242
     * @var \Doctrine\ORM\Utility\IdentifierFlattener
243
     */
244
    private $identifierFlattener;
245
246
    /**
247
     * Orphaned entities that are scheduled for removal.
248
     *
249
     * @var array
250
     */
251
    private $orphanRemovals = [];
252
253
    /**
254
     * Read-Only objects are never evaluated
255
     *
256
     * @var array
257
     */
258
    private $readOnlyObjects = [];
259
260
    /**
261
     * Map of Entity Class-Names and corresponding IDs that should eager loaded when requested.
262
     *
263
     * @var array
264
     */
265
    private $eagerLoadingEntities = [];
266
267
    /**
268
     * @var boolean
269
     */
270
    protected $hasCache = false;
271
272
    /**
273
     * Helper for handling completion of hydration
274
     *
275
     * @var HydrationCompleteHandler
276
     */
277
    private $hydrationCompleteHandler;
278
279
    /**
280
     * @var ReflectionPropertiesGetter
281
     */
282
    private $reflectionPropertiesGetter;
283
284
    /**
285
     * Initializes a new UnitOfWork instance, bound to the given EntityManager.
286
     *
287
     * @param EntityManagerInterface $em
288
     */
289 2401
    public function __construct(EntityManagerInterface $em)
290
    {
291 2401
        $this->em                         = $em;
292 2401
        $this->evm                        = $em->getEventManager();
293 2401
        $this->listenersInvoker           = new ListenersInvoker($em);
294 2401
        $this->hasCache                   = $em->getConfiguration()->isSecondLevelCacheEnabled();
295 2401
        $this->identifierFlattener        = new IdentifierFlattener($this, $em->getMetadataFactory());
296 2401
        $this->hydrationCompleteHandler   = new HydrationCompleteHandler($this->listenersInvoker, $em);
297 2401
        $this->reflectionPropertiesGetter = new ReflectionPropertiesGetter(new RuntimeReflectionService());
298 2401
    }
299
300
    /**
301
     * Commits the UnitOfWork, executing all operations that have been postponed
302
     * up to this point. The state of all managed entities will be synchronized with
303
     * the database.
304
     *
305
     * The operations are executed in the following order:
306
     *
307
     * 1) All entity insertions
308
     * 2) All entity updates
309
     * 3) All collection deletions
310
     * 4) All collection updates
311
     * 5) All entity deletions
312
     *
313
     * @param null|object|array $entity
314
     *
315
     * @return void
316
     *
317
     * @throws \Exception
318
     */
319 1040
    public function commit($entity = null)
320
    {
321
        // Raise preFlush
322 1040
        if ($this->evm->hasListeners(Events::preFlush)) {
323 2
            $this->evm->dispatchEvent(Events::preFlush, new PreFlushEventArgs($this->em));
324
        }
325
326
        // Compute changes done since last commit.
327 1040
        if (null === $entity) {
328 1030
            $this->computeChangeSets();
329 18
        } elseif (is_object($entity)) {
330 16
            $this->computeSingleEntityChangeSet($entity);
331 2
        } elseif (is_array($entity)) {
332 2
            foreach ($entity as $object) {
333 2
                $this->computeSingleEntityChangeSet($object);
334
            }
335
        }
336
337 1037
        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...
338 168
                $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...
339 132
                $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...
340 40
                $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...
341 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...
342 1037
                $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...
343 25
            $this->dispatchOnFlushEvent();
344 25
            $this->dispatchPostFlushEvent();
345
346 25
            return; // Nothing to do.
347
        }
348
349 1033
        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...
350 16
            foreach ($this->orphanRemovals as $orphan) {
351 16
                $this->remove($orphan);
352
            }
353
        }
354
355 1033
        $this->dispatchOnFlushEvent();
356
357
        // Now we need a commit order to maintain referential integrity
358 1033
        $commitOrder = $this->getCommitOrder();
359
360 1033
        $conn = $this->em->getConnection();
361 1033
        $conn->beginTransaction();
362
363
        try {
364
            // Collection deletions (deletions of complete collections)
365 1033
            foreach ($this->collectionDeletions as $collectionToDelete) {
366 19
                $this->getCollectionPersister($collectionToDelete->getMapping())->delete($collectionToDelete);
367
            }
368
369 1033
            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...
370 1029
                foreach ($commitOrder as $class) {
371 1029
                    $this->executeInserts($class);
372
                }
373
            }
374
375 1032
            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...
376 118
                foreach ($commitOrder as $class) {
377 118
                    $this->executeUpdates($class);
378
                }
379
            }
380
381
            // Extra updates that were requested by persisters.
382 1028
            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...
383 44
                $this->executeExtraUpdates();
384
            }
385
386
            // Collection updates (deleteRows, updateRows, insertRows)
387 1028
            foreach ($this->collectionUpdates as $collectionToUpdate) {
388 535
                $this->getCollectionPersister($collectionToUpdate->getMapping())->update($collectionToUpdate);
389
            }
390
391
            // Entity deletions come last and need to be in reverse commit order
392 1028
            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...
393 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...
394 63
                    $this->executeDeletions($commitOrder[$i]);
395
                }
396
            }
397
398 1028
            $conn->commit();
399 11
        } catch (Exception $e) {
400 11
            $this->em->close();
401 11
            $conn->rollBack();
402
403 11
            $this->afterTransactionRolledBack();
404
405 11
            throw $e;
406
        }
407
408 1028
        $this->afterTransactionComplete();
409
410
        // Take new snapshots from visited collections
411 1028
        foreach ($this->visitedCollections as $coll) {
412 534
            $coll->takeSnapshot();
413
        }
414
415 1028
        $this->dispatchPostFlushEvent();
416
417 1027
        $this->postCommitCleanup($entity);
0 ignored issues
show
Bug introduced by
It seems like $entity defined by parameter $entity on line 319 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...
418 1027
    }
419
420
    /**
421
     * @param null|object|object[] $entity
422
     */
423 1027
    private function postCommitCleanup($entity) : void
424
    {
425 1027
        $this->entityInsertions =
426 1027
        $this->entityUpdates =
427 1027
        $this->entityDeletions =
428 1027
        $this->extraUpdates =
429 1027
        $this->collectionUpdates =
430 1027
        $this->collectionDeletions =
431 1027
        $this->visitedCollections =
432 1027
        $this->orphanRemovals = [];
433
434 1027
        if (null === $entity) {
435 1018
            $this->entityChangeSets = $this->scheduledForSynchronization = [];
436
437 1018
            return;
438
        }
439
440 15
        $entities = \is_object($entity)
441 13
            ? [$entity]
442 15
            : $entity;
443
444 15
        foreach ($entities as $object) {
445 15
            $oid = \spl_object_hash($object);
446
447 15
            $this->clearEntityChangeSet($oid);
448
449 15
            unset($this->scheduledForSynchronization[$this->em->getClassMetadata(\get_class($object))->rootEntityName][$oid]);
450
        }
451 15
    }
452
453
    /**
454
     * Computes the changesets of all entities scheduled for insertion.
455
     *
456
     * @return void
457
     */
458 1039
    private function computeScheduleInsertsChangeSets()
459
    {
460 1039
        foreach ($this->entityInsertions as $entity) {
461 1031
            $class = $this->em->getClassMetadata(get_class($entity));
462
463 1031
            $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...
464
        }
465 1037
    }
466
467
    /**
468
     * Only flushes the given entity according to a ruleset that keeps the UoW consistent.
469
     *
470
     * 1. All entities scheduled for insertion, (orphan) removals and changes in collections are processed as well!
471
     * 2. Read Only entities are skipped.
472
     * 3. Proxies are skipped.
473
     * 4. Only if entity is properly managed.
474
     *
475
     * @param object $entity
476
     *
477
     * @return void
478
     *
479
     * @throws \InvalidArgumentException
480
     */
481 18
    private function computeSingleEntityChangeSet($entity)
482
    {
483 18
        $state = $this->getEntityState($entity);
484
485 18
        if ($state !== self::STATE_MANAGED && $state !== self::STATE_REMOVED) {
486 1
            throw new \InvalidArgumentException("Entity has to be managed or scheduled for removal for single computation " . self::objToStr($entity));
487
        }
488
489 17
        $class = $this->em->getClassMetadata(get_class($entity));
490
491 17
        if ($state === self::STATE_MANAGED && $class->isChangeTrackingDeferredImplicit()) {
492 16
            $this->persist($entity);
493
        }
494
495
        // Compute changes for INSERTed entities first. This must always happen even in this case.
496 17
        $this->computeScheduleInsertsChangeSets();
497
498 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...
499
            return;
500
        }
501
502
        // Ignore uninitialized proxy objects
503 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...
504 2
            return;
505
        }
506
507
        // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here.
508 15
        $oid = spl_object_hash($entity);
509
510 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...
511 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...
512
        }
513 14
    }
514
515
    /**
516
     * Executes any extra updates that have been scheduled.
517
     */
518 44
    private function executeExtraUpdates()
519
    {
520 44
        foreach ($this->extraUpdates as $oid => $update) {
521 44
            list ($entity, $changeset) = $update;
522
523 44
            $this->entityChangeSets[$oid] = $changeset;
524 44
            $this->getEntityPersister(get_class($entity))->update($entity);
525
        }
526
527 44
        $this->extraUpdates = [];
528 44
    }
529
530
    /**
531
     * Gets the changeset for an entity.
532
     *
533
     * @param object $entity
534
     *
535
     * @return array
536
     */
537 1031
    public function & getEntityChangeSet($entity)
538
    {
539 1031
        $oid  = spl_object_hash($entity);
540 1031
        $data = [];
541
542 1031
        if (!isset($this->entityChangeSets[$oid])) {
543 3
            return $data;
544
        }
545
546 1031
        return $this->entityChangeSets[$oid];
547
    }
548
549
    /**
550
     * Computes the changes that happened to a single entity.
551
     *
552
     * Modifies/populates the following properties:
553
     *
554
     * {@link _originalEntityData}
555
     * If the entity is NEW or MANAGED but not yet fully persisted (only has an id)
556
     * then it was not fetched from the database and therefore we have no original
557
     * entity data yet. All of the current entity data is stored as the original entity data.
558
     *
559
     * {@link _entityChangeSets}
560
     * The changes detected on all properties of the entity are stored there.
561
     * A change is a tuple array where the first entry is the old value and the second
562
     * entry is the new value of the property. Changesets are used by persisters
563
     * to INSERT/UPDATE the persistent entity state.
564
     *
565
     * {@link _entityUpdates}
566
     * If the entity is already fully MANAGED (has been fetched from the database before)
567
     * and any changes to its properties are detected, then a reference to the entity is stored
568
     * there to mark it for an update.
569
     *
570
     * {@link _collectionDeletions}
571
     * If a PersistentCollection has been de-referenced in a fully MANAGED entity,
572
     * then this collection is marked for deletion.
573
     *
574
     * @ignore
575
     *
576
     * @internal Don't call from the outside.
577
     *
578
     * @param ClassMetadata $class  The class descriptor of the entity.
579
     * @param object        $entity The entity for which to compute the changes.
580
     *
581
     * @return void
582
     */
583 1041
    public function computeChangeSet(ClassMetadata $class, $entity)
584
    {
585 1041
        $oid = spl_object_hash($entity);
586
587 1041
        if (isset($this->readOnlyObjects[$oid])) {
588 2
            return;
589
        }
590
591 1041
        if ( ! $class->isInheritanceTypeNone()) {
592 316
            $class = $this->em->getClassMetadata(get_class($entity));
593
        }
594
595 1041
        $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...
596
597 1041 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...
598 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...
599
        }
600
601 1041
        $actualData = [];
602
603 1041
        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...
604 1041
            $value = $refProp->getValue($entity);
605
606 1041
            if ($class->isCollectionValuedAssociation($name) && $value !== null) {
607 792
                if ($value instanceof PersistentCollection) {
608 202
                    if ($value->getOwner() === $entity) {
609 202
                        continue;
610
                    }
611
612 5
                    $value = new ArrayCollection($value->getValues());
613
                }
614
615
                // If $value is not a Collection then use an ArrayCollection.
616 787
                if ( ! $value instanceof Collection) {
617 242
                    $value = new ArrayCollection($value);
618
                }
619
620 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...
621
622
                // Inject PersistentCollection
623 787
                $value = new PersistentCollection(
624 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...
625
                );
626 787
                $value->setOwner($entity, $assoc);
627 787
                $value->setDirty( ! $value->isEmpty());
628
629 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...
630
631 787
                $actualData[$name] = $value;
632
633 787
                continue;
634
            }
635
636 1041
            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...
637 1041
                $actualData[$name] = $value;
638
            }
639
        }
640
641 1041
        if ( ! isset($this->originalEntityData[$oid])) {
642
            // Entity is either NEW or MANAGED but not yet fully persisted (only has an id).
643
            // These result in an INSERT.
644 1037
            $this->originalEntityData[$oid] = $actualData;
645 1037
            $changeSet = [];
646
647 1037
            foreach ($actualData as $propName => $actualValue) {
648 1020 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...
649 968
                    $changeSet[$propName] = [null, $actualValue];
650
651 968
                    continue;
652
                }
653
654 912
                $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...
655
656 912
                if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
657 912
                    $changeSet[$propName] = [null, $actualValue];
658
                }
659
            }
660
661 1037
            $this->entityChangeSets[$oid] = $changeSet;
662
        } else {
663
            // Entity is "fully" MANAGED: it was already fully persisted before
664
            // and we have a copy of the original data
665 269
            $originalData           = $this->originalEntityData[$oid];
666 269
            $isChangeTrackingNotify = $class->isChangeTrackingNotify();
667 269
            $changeSet              = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid]))
668
                ? $this->entityChangeSets[$oid]
669 269
                : [];
670
671 269
            foreach ($actualData as $propName => $actualValue) {
672
                // skip field, its a partially omitted one!
673 254
                if ( ! (isset($originalData[$propName]) || array_key_exists($propName, $originalData))) {
674 8
                    continue;
675
                }
676
677 254
                $orgValue = $originalData[$propName];
678
679
                // skip if value haven't changed
680 254
                if ($orgValue === $actualValue) {
681 238
                    continue;
682
                }
683
684
                // if regular field
685 114 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...
686 60
                    if ($isChangeTrackingNotify) {
687
                        continue;
688
                    }
689
690 60
                    $changeSet[$propName] = [$orgValue, $actualValue];
691
692 60
                    continue;
693
                }
694
695 58
                $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...
696
697
                // Persistent collection was exchanged with the "originally"
698
                // created one. This can only mean it was cloned and replaced
699
                // on another entity.
700 58
                if ($actualValue instanceof PersistentCollection) {
701 8
                    $owner = $actualValue->getOwner();
702 8
                    if ($owner === null) { // cloned
703
                        $actualValue->setOwner($entity, $assoc);
704 8
                    } else if ($owner !== $entity) { // no clone, we have to fix
705
                        if (!$actualValue->isInitialized()) {
706
                            $actualValue->initialize(); // we have to do this otherwise the cols share state
707
                        }
708
                        $newValue = clone $actualValue;
709
                        $newValue->setOwner($entity, $assoc);
710
                        $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...
711
                    }
712
                }
713
714 58
                if ($orgValue instanceof PersistentCollection) {
715
                    // A PersistentCollection was de-referenced, so delete it.
716 8
                    $coid = spl_object_hash($orgValue);
717
718 8
                    if (isset($this->collectionDeletions[$coid])) {
719
                        continue;
720
                    }
721
722 8
                    $this->collectionDeletions[$coid] = $orgValue;
723 8
                    $changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored.
724
725 8
                    continue;
726
                }
727
728 50
                if ($assoc['type'] & ClassMetadata::TO_ONE) {
729 49
                    if ($assoc['isOwningSide']) {
730 21
                        $changeSet[$propName] = [$orgValue, $actualValue];
731
                    }
732
733 49
                    if ($orgValue !== null && $assoc['orphanRemoval']) {
734 50
                        $this->scheduleOrphanRemoval($orgValue);
735
                    }
736
                }
737
            }
738
739 269
            if ($changeSet) {
740 87
                $this->entityChangeSets[$oid]   = $changeSet;
741 87
                $this->originalEntityData[$oid] = $actualData;
742 87
                $this->entityUpdates[$oid]      = $entity;
743
            }
744
        }
745
746
        // Look for changes in associations of the entity
747 1041
        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...
748 912
            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...
749 642
                continue;
750
            }
751
752 883
            $this->computeAssociationChanges($assoc, $val);
753
754 875
            if ( ! isset($this->entityChangeSets[$oid]) &&
755 875
                $assoc['isOwningSide'] &&
756 875
                $assoc['type'] == ClassMetadata::MANY_TO_MANY &&
757 875
                $val instanceof PersistentCollection &&
758 875
                $val->isDirty()) {
759
760 35
                $this->entityChangeSets[$oid]   = [];
761 35
                $this->originalEntityData[$oid] = $actualData;
762 875
                $this->entityUpdates[$oid]      = $entity;
763
            }
764
        }
765 1033
    }
766
767
    /**
768
     * Computes all the changes that have been done to entities and collections
769
     * since the last commit and stores these changes in the _entityChangeSet map
770
     * temporarily for access by the persisters, until the UoW commit is finished.
771
     *
772
     * @return void
773
     */
774 1030
    public function computeChangeSets()
775
    {
776
        // Compute changes for INSERTed entities first. This must always happen.
777 1030
        $this->computeScheduleInsertsChangeSets();
778
779
        // Compute changes for other MANAGED entities. Change tracking policies take effect here.
780 1028
        foreach ($this->identityMap as $className => $entities) {
781 456
            $class = $this->em->getClassMetadata($className);
782
783
            // Skip class if instances are read-only
784 456
            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...
785 1
                continue;
786
            }
787
788
            // If change tracking is explicit or happens through notification, then only compute
789
            // changes on entities of that type that are explicitly marked for synchronization.
790
            switch (true) {
791 455
                case ($class->isChangeTrackingDeferredImplicit()):
792 453
                    $entitiesToProcess = $entities;
793 453
                    break;
794
795 3
                case (isset($this->scheduledForSynchronization[$className])):
796 3
                    $entitiesToProcess = $this->scheduledForSynchronization[$className];
797 3
                    break;
798
799
                default:
800 1
                    $entitiesToProcess = [];
801
802
            }
803
804 455
            foreach ($entitiesToProcess as $entity) {
805
                // Ignore uninitialized proxy objects
806 435
                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...
807 36
                    continue;
808
                }
809
810
                // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here.
811 434
                $oid = spl_object_hash($entity);
812
813 434 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...
814 455
                    $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...
815
                }
816
            }
817
        }
818 1028
    }
819
820
    /**
821
     * Computes the changes of an association.
822
     *
823
     * @param array $assoc The association mapping.
824
     * @param mixed $value The value of the association.
825
     *
826
     * @throws ORMInvalidArgumentException
827
     * @throws ORMException
828
     *
829
     * @return void
830
     */
831 883
    private function computeAssociationChanges($assoc, $value)
832
    {
833 883
        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...
834 29
            return;
835
        }
836
837 882
        if ($value instanceof PersistentCollection && $value->isDirty()) {
838 537
            $coid = spl_object_hash($value);
839
840 537
            $this->collectionUpdates[$coid] = $value;
841 537
            $this->visitedCollections[$coid] = $value;
842
        }
843
844
        // Look through the entities, and in any of their associations,
845
        // 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...
846
        // Unwrap. Uninitialized collections will simply be empty.
847 882
        $unwrappedValue = ($assoc['type'] & ClassMetadata::TO_ONE) ? [$value] : $value->unwrap();
848 882
        $targetClass    = $this->em->getClassMetadata($assoc['targetEntity']);
849
850 882
        foreach ($unwrappedValue as $key => $entry) {
851 734
            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...
852 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...
853
            }
854
855 728
            $state = $this->getEntityState($entry, self::STATE_NEW);
856
857 728
            if ( ! ($entry instanceof $assoc['targetEntity'])) {
858
                throw ORMException::unexpectedAssociationValue($assoc['sourceEntity'], $assoc['fieldName'], get_class($entry), $assoc['targetEntity']);
859
            }
860
861
            switch ($state) {
862 728
                case self::STATE_NEW:
863 40
                    if ( ! $assoc['isCascadePersist']) {
864 4
                        throw ORMInvalidArgumentException::newEntityFoundThroughRelationship($assoc, $entry);
865
                    }
866
867 36
                    $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...
868 36
                    $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...
869 36
                    break;
870
871 722
                case self::STATE_REMOVED:
872
                    // Consume the $value as array (it's either an array or an ArrayAccess)
873
                    // and remove the element from Collection.
874 4
                    if ($assoc['type'] & ClassMetadata::TO_MANY) {
875 3
                        unset($value[$key]);
876
                    }
877 4
                    break;
878
879 722
                case self::STATE_DETACHED:
880
                    // Can actually not happen right now as we assume STATE_NEW,
881
                    // so the exception will be raised from the DBAL layer (constraint violation).
882
                    throw ORMInvalidArgumentException::detachedEntityFoundThroughRelationship($assoc, $entry);
883
                    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...
884
885 725
                default:
886
                    // MANAGED associated entities are already taken into account
887
                    // during changeset calculation anyway, since they are in the identity map.
888
            }
889
        }
890 874
    }
891
892
    /**
893
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
894
     * @param object                              $entity
895
     *
896
     * @return void
897
     */
898 1060
    private function persistNew($class, $entity)
899
    {
900 1060
        $oid    = spl_object_hash($entity);
901 1060
        $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::prePersist);
902
903 1060 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...
904 141
            $this->listenersInvoker->invoke($class, Events::prePersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
905
        }
906
907 1060
        $idGen = $class->idGenerator;
908
909 1060
        if ( ! $idGen->isPostInsertGenerator()) {
910 276
            $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...
911
912 276
            if ( ! $idGen instanceof \Doctrine\ORM\Id\AssignedGenerator) {
913 2
                $idValue = [$class->getSingleIdentifierFieldName() => $this->convertSingleFieldIdentifierToPHPValue($class, $idValue)];
914
915 2
                $class->setIdentifierValues($entity, $idValue);
916
            }
917
918 276
            $this->entityIdentifiers[$oid] = $idValue;
919
        }
920
921 1060
        $this->entityStates[$oid] = self::STATE_MANAGED;
922
923 1060
        $this->scheduleForInsert($entity);
924 1060
    }
925
926
    /**
927
     * INTERNAL:
928
     * Computes the changeset of an individual entity, independently of the
929
     * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit().
930
     *
931
     * The passed entity must be a managed entity. If the entity already has a change set
932
     * because this method is invoked during a commit cycle then the change sets are added.
933
     * whereby changes detected in this method prevail.
934
     *
935
     * @ignore
936
     *
937
     * @param ClassMetadata $class  The class descriptor of the entity.
938
     * @param object        $entity The entity for which to (re)calculate the change set.
939
     *
940
     * @return void
941
     *
942
     * @throws ORMInvalidArgumentException If the passed entity is not MANAGED.
943
     */
944 16
    public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity)
945
    {
946 16
        $oid = spl_object_hash($entity);
947
948 16
        if ( ! isset($this->entityStates[$oid]) || $this->entityStates[$oid] != self::STATE_MANAGED) {
949
            throw ORMInvalidArgumentException::entityNotManaged($entity);
950
        }
951
952
        // skip if change tracking is "NOTIFY"
953 16
        if ($class->isChangeTrackingNotify()) {
954
            return;
955
        }
956
957 16
        if ( ! $class->isInheritanceTypeNone()) {
958 3
            $class = $this->em->getClassMetadata(get_class($entity));
959
        }
960
961 16
        $actualData = [];
962
963 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...
964 16
            if (( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity())
965 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...
966 16
                && ! $class->isCollectionValuedAssociation($name)) {
967 16
                $actualData[$name] = $refProp->getValue($entity);
968
            }
969
        }
970
971 16
        if ( ! isset($this->originalEntityData[$oid])) {
972
            throw new \RuntimeException('Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.');
973
        }
974
975 16
        $originalData = $this->originalEntityData[$oid];
976 16
        $changeSet = [];
977
978 16
        foreach ($actualData as $propName => $actualValue) {
979 16
            $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
980
981 16
            if ($orgValue !== $actualValue) {
982 16
                $changeSet[$propName] = [$orgValue, $actualValue];
983
            }
984
        }
985
986 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...
987 7
            if (isset($this->entityChangeSets[$oid])) {
988 6
                $this->entityChangeSets[$oid] = array_merge($this->entityChangeSets[$oid], $changeSet);
989 1
            } else if ( ! isset($this->entityInsertions[$oid])) {
990 1
                $this->entityChangeSets[$oid] = $changeSet;
991 1
                $this->entityUpdates[$oid]    = $entity;
992
            }
993 7
            $this->originalEntityData[$oid] = $actualData;
994
        }
995 16
    }
996
997
    /**
998
     * Executes all entity insertions for entities of the specified type.
999
     *
1000
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
1001
     *
1002
     * @return void
1003
     */
1004 1029
    private function executeInserts($class)
1005
    {
1006 1029
        $entities   = [];
1007 1029
        $className  = $class->name;
1008 1029
        $persister  = $this->getEntityPersister($className);
1009 1029
        $invoke     = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist);
1010
1011 1029
        foreach ($this->entityInsertions as $oid => $entity) {
1012
1013 1029
            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...
1014 873
                continue;
1015
            }
1016
1017 1029
            $persister->addInsert($entity);
1018
1019 1029
            unset($this->entityInsertions[$oid]);
1020
1021 1029
            if ($invoke !== ListenersInvoker::INVOKE_NONE) {
1022 1029
                $entities[] = $entity;
1023
            }
1024
        }
1025
1026 1029
        $postInsertIds = $persister->executeInserts();
1027
1028 1029
        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...
1029
            // Persister returned post-insert IDs
1030 938
            foreach ($postInsertIds as $postInsertId) {
1031 938
                $idField = $class->getSingleIdentifierFieldName();
1032 938
                $idValue = $this->convertSingleFieldIdentifierToPHPValue($class, $postInsertId['generatedId']);
1033
1034 938
                $entity  = $postInsertId['entity'];
1035 938
                $oid     = spl_object_hash($entity);
1036
1037 938
                $class->reflFields[$idField]->setValue($entity, $idValue);
1038
1039 938
                $this->entityIdentifiers[$oid] = [$idField => $idValue];
1040 938
                $this->entityStates[$oid] = self::STATE_MANAGED;
1041 938
                $this->originalEntityData[$oid][$idField] = $idValue;
1042
1043 938
                $this->addToIdentityMap($entity);
1044
            }
1045
        }
1046
1047 1029
        foreach ($entities as $entity) {
1048 136
            $this->listenersInvoker->invoke($class, Events::postPersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
1049
        }
1050 1029
    }
1051
1052
    /**
1053
     * Executes all entity updates for entities of the specified type.
1054
     *
1055
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
1056
     *
1057
     * @return void
1058
     */
1059 118
    private function executeUpdates($class)
1060
    {
1061 118
        $className          = $class->name;
1062 118
        $persister          = $this->getEntityPersister($className);
1063 118
        $preUpdateInvoke    = $this->listenersInvoker->getSubscribedSystems($class, Events::preUpdate);
1064 118
        $postUpdateInvoke   = $this->listenersInvoker->getSubscribedSystems($class, Events::postUpdate);
1065
1066 118
        foreach ($this->entityUpdates as $oid => $entity) {
1067 118
            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...
1068 76
                continue;
1069
            }
1070
1071 118
            if ($preUpdateInvoke != ListenersInvoker::INVOKE_NONE) {
1072 13
                $this->listenersInvoker->invoke($class, Events::preUpdate, $entity, new PreUpdateEventArgs($entity, $this->em, $this->getEntityChangeSet($entity)), $preUpdateInvoke);
1073
1074 13
                $this->recomputeSingleEntityChangeSet($class, $entity);
1075
            }
1076
1077 118
            if ( ! empty($this->entityChangeSets[$oid])) {
1078 84
                $persister->update($entity);
1079
            }
1080
1081 114
            unset($this->entityUpdates[$oid]);
1082
1083 114 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...
1084 114
                $this->listenersInvoker->invoke($class, Events::postUpdate, $entity, new LifecycleEventArgs($entity, $this->em), $postUpdateInvoke);
1085
            }
1086
        }
1087 114
    }
1088
1089
    /**
1090
     * Executes all entity deletions for entities of the specified type.
1091
     *
1092
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
1093
     *
1094
     * @return void
1095
     */
1096 63
    private function executeDeletions($class)
1097
    {
1098 63
        $className  = $class->name;
1099 63
        $persister  = $this->getEntityPersister($className);
1100 63
        $invoke     = $this->listenersInvoker->getSubscribedSystems($class, Events::postRemove);
1101
1102 63
        foreach ($this->entityDeletions as $oid => $entity) {
1103 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...
1104 25
                continue;
1105
            }
1106
1107 63
            $persister->delete($entity);
1108
1109
            unset(
1110 63
                $this->entityDeletions[$oid],
1111 63
                $this->entityIdentifiers[$oid],
1112 63
                $this->originalEntityData[$oid],
1113 63
                $this->entityStates[$oid]
1114
            );
1115
1116
            // Entity with this $oid after deletion treated as NEW, even if the $oid
1117
            // is obtained by a new entity because the old one went out of scope.
1118
            //$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...
1119 63
            if ( ! $class->isIdentifierNatural()) {
1120 53
                $class->reflFields[$class->identifier[0]]->setValue($entity, null);
1121
            }
1122
1123 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...
1124 63
                $this->listenersInvoker->invoke($class, Events::postRemove, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
1125
            }
1126
        }
1127 62
    }
1128
1129
    /**
1130
     * Gets the commit order.
1131
     *
1132
     * @param array|null $entityChangeSet
1133
     *
1134
     * @return array
1135
     */
1136 1033
    private function getCommitOrder(array $entityChangeSet = null)
1137
    {
1138 1033
        if ($entityChangeSet === null) {
1139 1033
            $entityChangeSet = array_merge($this->entityInsertions, $this->entityUpdates, $this->entityDeletions);
1140
        }
1141
1142 1033
        $calc = $this->getCommitOrderCalculator();
1143
1144
        // See if there are any new classes in the changeset, that are not in the
1145
        // commit order graph yet (don't have a node).
1146
        // We have to inspect changeSet to be able to correctly build dependencies.
1147
        // It is not possible to use IdentityMap here because post inserted ids
1148
        // are not yet available.
1149 1033
        $newNodes = [];
1150
1151 1033
        foreach ($entityChangeSet as $entity) {
1152 1033
            $class = $this->em->getClassMetadata(get_class($entity));
1153
1154 1033
            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...
1155 636
                continue;
1156
            }
1157
1158 1033
            $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...
1159
1160 1033
            $newNodes[] = $class;
1161
        }
1162
1163
        // Calculate dependencies for new nodes
1164 1033
        while ($class = array_pop($newNodes)) {
1165 1033
            foreach ($class->associationMappings as $assoc) {
1166 904
                if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) {
1167 863
                    continue;
1168
                }
1169
1170 855
                $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
1171
1172 855
                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...
1173 659
                    $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...
1174
1175 659
                    $newNodes[] = $targetClass;
1176
                }
1177
1178 855
                $joinColumns = reset($assoc['joinColumns']);
1179
1180 855
                $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...
1181
1182
                // If the target class has mapped subclasses, these share the same dependency.
1183 855
                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...
1184 848
                    continue;
1185
                }
1186
1187 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...
1188 226
                    $targetSubClass = $this->em->getClassMetadata($subClassName);
1189
1190 226
                    if ( ! $calc->hasNode($subClassName)) {
1191 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...
1192
1193 196
                        $newNodes[] = $targetSubClass;
1194
                    }
1195
1196 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...
1197
                }
1198
            }
1199
        }
1200
1201 1033
        return $calc->sort();
1202
    }
1203
1204
    /**
1205
     * Schedules an entity for insertion into the database.
1206
     * If the entity already has an identifier, it will be added to the identity map.
1207
     *
1208
     * @param object $entity The entity to schedule for insertion.
1209
     *
1210
     * @return void
1211
     *
1212
     * @throws ORMInvalidArgumentException
1213
     * @throws \InvalidArgumentException
1214
     */
1215 1061
    public function scheduleForInsert($entity)
1216
    {
1217 1061
        $oid = spl_object_hash($entity);
1218
1219 1061
        if (isset($this->entityUpdates[$oid])) {
1220
            throw new InvalidArgumentException("Dirty entity can not be scheduled for insertion.");
1221
        }
1222
1223 1061
        if (isset($this->entityDeletions[$oid])) {
1224 1
            throw ORMInvalidArgumentException::scheduleInsertForRemovedEntity($entity);
1225
        }
1226 1061
        if (isset($this->originalEntityData[$oid]) && ! isset($this->entityInsertions[$oid])) {
1227 1
            throw ORMInvalidArgumentException::scheduleInsertForManagedEntity($entity);
1228
        }
1229
1230 1061
        if (isset($this->entityInsertions[$oid])) {
1231 1
            throw ORMInvalidArgumentException::scheduleInsertTwice($entity);
1232
        }
1233
1234 1061
        $this->entityInsertions[$oid] = $entity;
1235
1236 1061
        if (isset($this->entityIdentifiers[$oid])) {
1237 276
            $this->addToIdentityMap($entity);
1238
        }
1239
1240 1061
        if ($entity instanceof NotifyPropertyChanged) {
1241 7
            $entity->addPropertyChangedListener($this);
1242
        }
1243 1061
    }
1244
1245
    /**
1246
     * Checks whether an entity is scheduled for insertion.
1247
     *
1248
     * @param object $entity
1249
     *
1250
     * @return boolean
1251
     */
1252 645
    public function isScheduledForInsert($entity)
1253
    {
1254 645
        return isset($this->entityInsertions[spl_object_hash($entity)]);
1255
    }
1256
1257
    /**
1258
     * Schedules an entity for being updated.
1259
     *
1260
     * @param object $entity The entity to schedule for being updated.
1261
     *
1262
     * @return void
1263
     *
1264
     * @throws ORMInvalidArgumentException
1265
     */
1266 1
    public function scheduleForUpdate($entity)
1267
    {
1268 1
        $oid = spl_object_hash($entity);
1269
1270 1
        if ( ! isset($this->entityIdentifiers[$oid])) {
1271
            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "scheduling for update");
1272
        }
1273
1274 1
        if (isset($this->entityDeletions[$oid])) {
1275
            throw ORMInvalidArgumentException::entityIsRemoved($entity, "schedule for update");
1276
        }
1277
1278 1
        if ( ! isset($this->entityUpdates[$oid]) && ! isset($this->entityInsertions[$oid])) {
1279 1
            $this->entityUpdates[$oid] = $entity;
1280
        }
1281 1
    }
1282
1283
    /**
1284
     * INTERNAL:
1285
     * Schedules an extra update that will be executed immediately after the
1286
     * regular entity updates within the currently running commit cycle.
1287
     *
1288
     * Extra updates for entities are stored as (entity, changeset) tuples.
1289
     *
1290
     * @ignore
1291
     *
1292
     * @param object $entity    The entity for which to schedule an extra update.
1293
     * @param array  $changeset The changeset of the entity (what to update).
1294
     *
1295
     * @return void
1296
     */
1297 44
    public function scheduleExtraUpdate($entity, array $changeset)
1298
    {
1299 44
        $oid         = spl_object_hash($entity);
1300 44
        $extraUpdate = [$entity, $changeset];
1301
1302 44
        if (isset($this->extraUpdates[$oid])) {
1303 1
            list(, $changeset2) = $this->extraUpdates[$oid];
1304
1305 1
            $extraUpdate = [$entity, $changeset + $changeset2];
1306
        }
1307
1308 44
        $this->extraUpdates[$oid] = $extraUpdate;
1309 44
    }
1310
1311
    /**
1312
     * Checks whether an entity is registered as dirty in the unit of work.
1313
     * Note: Is not very useful currently as dirty entities are only registered
1314
     * at commit time.
1315
     *
1316
     * @param object $entity
1317
     *
1318
     * @return boolean
1319
     */
1320
    public function isScheduledForUpdate($entity)
1321
    {
1322
        return isset($this->entityUpdates[spl_object_hash($entity)]);
1323
    }
1324
1325
    /**
1326
     * Checks whether an entity is registered to be checked in the unit of work.
1327
     *
1328
     * @param object $entity
1329
     *
1330
     * @return boolean
1331
     */
1332 1
    public function isScheduledForDirtyCheck($entity)
1333
    {
1334 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...
1335
1336 1
        return isset($this->scheduledForSynchronization[$rootEntityName][spl_object_hash($entity)]);
1337
    }
1338
1339
    /**
1340
     * INTERNAL:
1341
     * Schedules an entity for deletion.
1342
     *
1343
     * @param object $entity
1344
     *
1345
     * @return void
1346
     */
1347 66
    public function scheduleForDelete($entity)
1348
    {
1349 66
        $oid = spl_object_hash($entity);
1350
1351 66
        if (isset($this->entityInsertions[$oid])) {
1352 1
            if ($this->isInIdentityMap($entity)) {
1353
                $this->removeFromIdentityMap($entity);
1354
            }
1355
1356 1
            unset($this->entityInsertions[$oid], $this->entityStates[$oid]);
1357
1358 1
            return; // entity has not been persisted yet, so nothing more to do.
1359
        }
1360
1361 66
        if ( ! $this->isInIdentityMap($entity)) {
1362 1
            return;
1363
        }
1364
1365 65
        $this->removeFromIdentityMap($entity);
1366
1367 65
        unset($this->entityUpdates[$oid]);
1368
1369 65
        if ( ! isset($this->entityDeletions[$oid])) {
1370 65
            $this->entityDeletions[$oid] = $entity;
1371 65
            $this->entityStates[$oid]    = self::STATE_REMOVED;
1372
        }
1373 65
    }
1374
1375
    /**
1376
     * Checks whether an entity is registered as removed/deleted with the unit
1377
     * of work.
1378
     *
1379
     * @param object $entity
1380
     *
1381
     * @return boolean
1382
     */
1383 17
    public function isScheduledForDelete($entity)
1384
    {
1385 17
        return isset($this->entityDeletions[spl_object_hash($entity)]);
1386
    }
1387
1388
    /**
1389
     * Checks whether an entity is scheduled for insertion, update or deletion.
1390
     *
1391
     * @param object $entity
1392
     *
1393
     * @return boolean
1394
     */
1395
    public function isEntityScheduled($entity)
1396
    {
1397
        $oid = spl_object_hash($entity);
1398
1399
        return isset($this->entityInsertions[$oid])
1400
            || isset($this->entityUpdates[$oid])
1401
            || isset($this->entityDeletions[$oid]);
1402
    }
1403
1404
    /**
1405
     * INTERNAL:
1406
     * Registers an entity in the identity map.
1407
     * Note that entities in a hierarchy are registered with the class name of
1408
     * the root entity.
1409
     *
1410
     * @ignore
1411
     *
1412
     * @param object $entity The entity to register.
1413
     *
1414
     * @return boolean TRUE if the registration was successful, FALSE if the identity of
1415
     *                 the entity in question is already managed.
1416
     *
1417
     * @throws ORMInvalidArgumentException
1418
     */
1419 1127
    public function addToIdentityMap($entity)
1420
    {
1421 1127
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1422 1127
        $identifier    = $this->entityIdentifiers[spl_object_hash($entity)];
1423
1424 1127
        if (empty($identifier) || in_array(null, $identifier, true)) {
1425 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...
1426
        }
1427
1428 1121
        $idHash    = implode(' ', $identifier);
1429 1121
        $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...
1430
1431 1121
        if (isset($this->identityMap[$className][$idHash])) {
1432 86
            return false;
1433
        }
1434
1435 1121
        $this->identityMap[$className][$idHash] = $entity;
1436
1437 1121
        return true;
1438
    }
1439
1440
    /**
1441
     * Gets the state of an entity with regard to the current unit of work.
1442
     *
1443
     * @param object   $entity
1444
     * @param int|null $assume The state to assume if the state is not yet known (not MANAGED or REMOVED).
1445
     *                         This parameter can be set to improve performance of entity state detection
1446
     *                         by potentially avoiding a database lookup if the distinction between NEW and DETACHED
1447
     *                         is either known or does not matter for the caller of the method.
1448
     *
1449
     * @return int The entity state.
1450
     */
1451 1075
    public function getEntityState($entity, $assume = null)
1452
    {
1453 1075
        $oid = spl_object_hash($entity);
1454
1455 1075
        if (isset($this->entityStates[$oid])) {
1456 800
            return $this->entityStates[$oid];
1457
        }
1458
1459 1069
        if ($assume !== null) {
1460 1065
            return $assume;
1461
        }
1462
1463
        // State can only be NEW or DETACHED, because MANAGED/REMOVED states are known.
1464
        // Note that you can not remember the NEW or DETACHED state in _entityStates since
1465
        // the UoW does not hold references to such objects and the object hash can be reused.
1466
        // More generally because the state may "change" between NEW/DETACHED without the UoW being aware of it.
1467 13
        $class = $this->em->getClassMetadata(get_class($entity));
1468 13
        $id    = $class->getIdentifierValues($entity);
1469
1470 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...
1471 5
            return self::STATE_NEW;
1472
        }
1473
1474 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...
1475 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...
1476
        }
1477
1478
        switch (true) {
1479 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...
1480
                // Check for a version field, if available, to avoid a db lookup.
1481 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...
1482 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...
1483
                        ? self::STATE_DETACHED
1484 1
                        : self::STATE_NEW;
1485
                }
1486
1487
                // Last try before db lookup: check the identity map.
1488 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...
1489 1
                    return self::STATE_DETACHED;
1490
                }
1491
1492
                // db lookup
1493 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...
1494
                    return self::STATE_DETACHED;
1495
                }
1496
1497 4
                return self::STATE_NEW;
1498
1499 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...
1500
                // if we have a pre insert generator we can't be sure that having an id
1501
                // really means that the entity exists. We have to verify this through
1502
                // the last resort: a db lookup
1503
1504
                // Last try before db lookup: check the identity map.
1505
                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...
1506
                    return self::STATE_DETACHED;
1507
                }
1508
1509
                // db lookup
1510
                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...
1511
                    return self::STATE_DETACHED;
1512
                }
1513
1514
                return self::STATE_NEW;
1515
1516
            default:
1517 5
                return self::STATE_DETACHED;
1518
        }
1519
    }
1520
1521
    /**
1522
     * INTERNAL:
1523
     * Removes an entity from the identity map. This effectively detaches the
1524
     * entity from the persistence management of Doctrine.
1525
     *
1526
     * @ignore
1527
     *
1528
     * @param object $entity
1529
     *
1530
     * @return boolean
1531
     *
1532
     * @throws ORMInvalidArgumentException
1533
     */
1534 78
    public function removeFromIdentityMap($entity)
1535
    {
1536 78
        $oid           = spl_object_hash($entity);
1537 78
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1538 78
        $idHash        = implode(' ', $this->entityIdentifiers[$oid]);
1539
1540 78
        if ($idHash === '') {
1541
            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "remove from identity map");
1542
        }
1543
1544 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...
1545
1546 78
        if (isset($this->identityMap[$className][$idHash])) {
1547 78
            unset($this->identityMap[$className][$idHash]);
1548 78
            unset($this->readOnlyObjects[$oid]);
1549
1550
            //$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...
1551
1552 78
            return true;
1553
        }
1554
1555
        return false;
1556
    }
1557
1558
    /**
1559
     * INTERNAL:
1560
     * Gets an entity in the identity map by its identifier hash.
1561
     *
1562
     * @ignore
1563
     *
1564
     * @param string $idHash
1565
     * @param string $rootClassName
1566
     *
1567
     * @return object
1568
     */
1569 6
    public function getByIdHash($idHash, $rootClassName)
1570
    {
1571 6
        return $this->identityMap[$rootClassName][$idHash];
1572
    }
1573
1574
    /**
1575
     * INTERNAL:
1576
     * Tries to get an entity by its identifier hash. If no entity is found for
1577
     * the given hash, FALSE is returned.
1578
     *
1579
     * @ignore
1580
     *
1581
     * @param mixed  $idHash        (must be possible to cast it to string)
1582
     * @param string $rootClassName
1583
     *
1584
     * @return object|bool The found entity or FALSE.
1585
     */
1586 35 View Code Duplication
    public function tryGetByIdHash($idHash, $rootClassName)
1587
    {
1588 35
        $stringIdHash = (string) $idHash;
1589
1590 35
        return isset($this->identityMap[$rootClassName][$stringIdHash])
1591 35
            ? $this->identityMap[$rootClassName][$stringIdHash]
1592 35
            : false;
1593
    }
1594
1595
    /**
1596
     * Checks whether an entity is registered in the identity map of this UnitOfWork.
1597
     *
1598
     * @param object $entity
1599
     *
1600
     * @return boolean
1601
     */
1602 216
    public function isInIdentityMap($entity)
1603
    {
1604 216
        $oid = spl_object_hash($entity);
1605
1606 216
        if (empty($this->entityIdentifiers[$oid])) {
1607 33
            return false;
1608
        }
1609
1610 200
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1611 200
        $idHash        = implode(' ', $this->entityIdentifiers[$oid]);
1612
1613 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...
1614
    }
1615
1616
    /**
1617
     * INTERNAL:
1618
     * Checks whether an identifier hash exists in the identity map.
1619
     *
1620
     * @ignore
1621
     *
1622
     * @param string $idHash
1623
     * @param string $rootClassName
1624
     *
1625
     * @return boolean
1626
     */
1627
    public function containsIdHash($idHash, $rootClassName)
1628
    {
1629
        return isset($this->identityMap[$rootClassName][$idHash]);
1630
    }
1631
1632
    /**
1633
     * Persists an entity as part of the current unit of work.
1634
     *
1635
     * @param object $entity The entity to persist.
1636
     *
1637
     * @return void
1638
     */
1639 1056
    public function persist($entity)
1640
    {
1641 1056
        $visited = [];
1642
1643 1056
        $this->doPersist($entity, $visited);
1644 1049
    }
1645
1646
    /**
1647
     * Persists an entity as part of the current unit of work.
1648
     *
1649
     * This method is internally called during persist() cascades as it tracks
1650
     * the already visited entities to prevent infinite recursions.
1651
     *
1652
     * @param object $entity  The entity to persist.
1653
     * @param array  $visited The already visited entities.
1654
     *
1655
     * @return void
1656
     *
1657
     * @throws ORMInvalidArgumentException
1658
     * @throws UnexpectedValueException
1659
     */
1660 1056
    private function doPersist($entity, array &$visited)
1661
    {
1662 1056
        $oid = spl_object_hash($entity);
1663
1664 1056
        if (isset($visited[$oid])) {
1665 110
            return; // Prevent infinite recursion
1666
        }
1667
1668 1056
        $visited[$oid] = $entity; // Mark visited
1669
1670 1056
        $class = $this->em->getClassMetadata(get_class($entity));
1671
1672
        // We assume NEW, so DETACHED entities result in an exception on flush (constraint violation).
1673
        // If we would detect DETACHED here we would throw an exception anyway with the same
1674
        // consequences (not recoverable/programming error), so just assuming NEW here
1675
        // lets us avoid some database lookups for entities with natural identifiers.
1676 1056
        $entityState = $this->getEntityState($entity, self::STATE_NEW);
1677
1678
        switch ($entityState) {
1679 1056
            case self::STATE_MANAGED:
1680
                // Nothing to do, except if policy is "deferred explicit"
1681 238
                if ($class->isChangeTrackingDeferredExplicit()) {
1682 2
                    $this->scheduleForDirtyCheck($entity);
1683
                }
1684 238
                break;
1685
1686 1056
            case self::STATE_NEW:
1687 1055
                $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...
1688 1055
                break;
1689
1690 1
            case self::STATE_REMOVED:
1691
                // Entity becomes managed again
1692 1
                unset($this->entityDeletions[$oid]);
1693 1
                $this->addToIdentityMap($entity);
1694
1695 1
                $this->entityStates[$oid] = self::STATE_MANAGED;
1696 1
                break;
1697
1698
            case self::STATE_DETACHED:
1699
                // Can actually not happen right now since we assume STATE_NEW.
1700
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "persisted");
1701
1702
            default:
1703
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1704
        }
1705
1706 1056
        $this->cascadePersist($entity, $visited);
1707 1049
    }
1708
1709
    /**
1710
     * Deletes an entity as part of the current unit of work.
1711
     *
1712
     * @param object $entity The entity to remove.
1713
     *
1714
     * @return void
1715
     */
1716 65
    public function remove($entity)
1717
    {
1718 65
        $visited = [];
1719
1720 65
        $this->doRemove($entity, $visited);
1721 65
    }
1722
1723
    /**
1724
     * Deletes an entity as part of the current unit of work.
1725
     *
1726
     * This method is internally called during delete() cascades as it tracks
1727
     * the already visited entities to prevent infinite recursions.
1728
     *
1729
     * @param object $entity  The entity to delete.
1730
     * @param array  $visited The map of the already visited entities.
1731
     *
1732
     * @return void
1733
     *
1734
     * @throws ORMInvalidArgumentException If the instance is a detached entity.
1735
     * @throws UnexpectedValueException
1736
     */
1737 65
    private function doRemove($entity, array &$visited)
1738
    {
1739 65
        $oid = spl_object_hash($entity);
1740
1741 65
        if (isset($visited[$oid])) {
1742 1
            return; // Prevent infinite recursion
1743
        }
1744
1745 65
        $visited[$oid] = $entity; // mark visited
1746
1747
        // Cascade first, because scheduleForDelete() removes the entity from the identity map, which
1748
        // can cause problems when a lazy proxy has to be initialized for the cascade operation.
1749 65
        $this->cascadeRemove($entity, $visited);
1750
1751 65
        $class       = $this->em->getClassMetadata(get_class($entity));
1752 65
        $entityState = $this->getEntityState($entity);
1753
1754
        switch ($entityState) {
1755 65
            case self::STATE_NEW:
1756 65
            case self::STATE_REMOVED:
1757
                // nothing to do
1758 2
                break;
1759
1760 65
            case self::STATE_MANAGED:
1761 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...
1762
1763 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...
1764 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...
1765
                }
1766
1767 65
                $this->scheduleForDelete($entity);
1768 65
                break;
1769
1770
            case self::STATE_DETACHED:
1771
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "removed");
1772
            default:
1773
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1774
        }
1775
1776 65
    }
1777
1778
    /**
1779
     * Merges the state of the given detached entity into this UnitOfWork.
1780
     *
1781
     * @param object $entity
1782
     *
1783
     * @return object The managed copy of the entity.
1784
     *
1785
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1786
     *         attribute and the version check against the managed copy fails.
1787
     *
1788
     * @todo Require active transaction!? OptimisticLockException may result in undefined state!?
1789
     */
1790 43
    public function merge($entity)
1791
    {
1792 43
        $visited = [];
1793
1794 43
        return $this->doMerge($entity, $visited);
1795
    }
1796
1797
    /**
1798
     * Executes a merge operation on an entity.
1799
     *
1800
     * @param object      $entity
1801
     * @param array       $visited
1802
     * @param object|null $prevManagedCopy
1803
     * @param array|null  $assoc
1804
     *
1805
     * @return object The managed copy of the entity.
1806
     *
1807
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1808
     *         attribute and the version check against the managed copy fails.
1809
     * @throws ORMInvalidArgumentException If the entity instance is NEW.
1810
     * @throws EntityNotFoundException if an assigned identifier is used in the entity, but none is provided
1811
     */
1812 43
    private function doMerge($entity, array &$visited, $prevManagedCopy = null, array $assoc = [])
1813
    {
1814 43
        $oid = spl_object_hash($entity);
1815
1816 43
        if (isset($visited[$oid])) {
1817 4
            $managedCopy = $visited[$oid];
1818
1819 4
            if ($prevManagedCopy !== null) {
1820 4
                $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy);
1821
            }
1822
1823 4
            return $managedCopy;
1824
        }
1825
1826 43
        $class = $this->em->getClassMetadata(get_class($entity));
1827
1828
        // First we assume DETACHED, although it can still be NEW but we can avoid
1829
        // an extra db-roundtrip this way. If it is not MANAGED but has an identity,
1830
        // we need to fetch it from the db anyway in order to merge.
1831
        // MANAGED entities are ignored by the merge operation.
1832 43
        $managedCopy = $entity;
1833
1834 43
        if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) {
1835
            // Try to look the entity up in the identity map.
1836 42
            $id = $class->getIdentifierValues($entity);
1837
1838
            // If there is no ID, it is actually NEW.
1839 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...
1840 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...
1841
1842 6
                $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy);
1843 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...
1844
            } else {
1845 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...
1846 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...
1847 37
                    : $id;
1848
1849 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 1898 which is incompatible with the return type documented by Doctrine\ORM\UnitOfWork::doMerge of type object.
Loading history...
1850
1851 37
                if ($managedCopy) {
1852
                    // We have the entity in-memory already, just make sure its not removed.
1853 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 1849 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...
1854 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 1849 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...
1855
                    }
1856
                } else {
1857
                    // We need to fetch the managed copy in order to merge.
1858 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...
1859
                }
1860
1861 37
                if ($managedCopy === null) {
1862
                    // If the identifier is ASSIGNED, it is NEW, otherwise an error
1863
                    // since the managed entity was not found.
1864 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...
1865 1
                        throw EntityNotFoundException::fromClassNameAndIdentifier(
1866 1
                            $class->getName(),
1867 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...
1868
                        );
1869
                    }
1870
1871 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...
1872 2
                    $class->setIdentifierValues($managedCopy, $id);
1873
1874 2
                    $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy);
1875 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...
1876
                } else {
1877 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 1849 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...
1878 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 1849 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...
1879
                }
1880
            }
1881
1882 40
            $visited[$oid] = $managedCopy; // mark visited
1883
1884 40
            if ($class->isChangeTrackingDeferredExplicit()) {
1885
                $this->scheduleForDirtyCheck($entity);
1886
            }
1887
        }
1888
1889 41
        if ($prevManagedCopy !== null) {
1890 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 1849 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...
1891
        }
1892
1893
        // Mark the managed copy visited as well
1894 41
        $visited[spl_object_hash($managedCopy)] = $managedCopy;
1895
1896 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 1849 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...
1897
1898 41
        return $managedCopy;
1899
    }
1900
1901
    /**
1902
     * @param ClassMetadata $class
1903
     * @param object        $entity
1904
     * @param object        $managedCopy
1905
     *
1906
     * @return void
1907
     *
1908
     * @throws OptimisticLockException
1909
     */
1910 34
    private function ensureVersionMatch(ClassMetadata $class, $entity, $managedCopy)
1911
    {
1912 34
        if (! ($class->isVersioned && $this->isLoaded($managedCopy) && $this->isLoaded($entity))) {
1913 31
            return;
1914
        }
1915
1916 4
        $reflField          = $class->reflFields[$class->versionField];
1917 4
        $managedCopyVersion = $reflField->getValue($managedCopy);
1918 4
        $entityVersion      = $reflField->getValue($entity);
1919
1920
        // Throw exception if versions don't match.
1921 4
        if ($managedCopyVersion == $entityVersion) {
1922 3
            return;
1923
        }
1924
1925 1
        throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion);
1926
    }
1927
1928
    /**
1929
     * Tests if an entity is loaded - must either be a loaded proxy or not a proxy
1930
     *
1931
     * @param object $entity
1932
     *
1933
     * @return bool
1934
     */
1935 41
    private function isLoaded($entity)
1936
    {
1937 41
        return !($entity instanceof Proxy) || $entity->__isInitialized();
1938
    }
1939
1940
    /**
1941
     * Sets/adds associated managed copies into the previous entity's association field
1942
     *
1943
     * @param object $entity
1944
     * @param array  $association
1945
     * @param object $previousManagedCopy
1946
     * @param object $managedCopy
1947
     *
1948
     * @return void
1949
     */
1950 6
    private function updateAssociationWithMergedEntity($entity, array $association, $previousManagedCopy, $managedCopy)
1951
    {
1952 6
        $assocField = $association['fieldName'];
1953 6
        $prevClass  = $this->em->getClassMetadata(get_class($previousManagedCopy));
1954
1955 6
        if ($association['type'] & ClassMetadata::TO_ONE) {
1956 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...
1957
1958 6
            return;
1959
        }
1960
1961 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...
1962 1
        $value[] = $managedCopy;
1963
1964 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...
1965 1
            $class = $this->em->getClassMetadata(get_class($entity));
1966
1967 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...
1968
        }
1969 1
    }
1970
1971
    /**
1972
     * Detaches an entity from the persistence management. It's persistence will
1973
     * no longer be managed by Doctrine.
1974
     *
1975
     * @param object $entity The entity to detach.
1976
     *
1977
     * @return void
1978
     */
1979 12
    public function detach($entity)
1980
    {
1981 12
        $visited = [];
1982
1983 12
        $this->doDetach($entity, $visited);
1984 12
    }
1985
1986
    /**
1987
     * Executes a detach operation on the given entity.
1988
     *
1989
     * @param object  $entity
1990
     * @param array   $visited
1991
     * @param boolean $noCascade if true, don't cascade detach operation.
1992
     *
1993
     * @return void
1994
     */
1995 16
    private function doDetach($entity, array &$visited, $noCascade = false)
1996
    {
1997 16
        $oid = spl_object_hash($entity);
1998
1999 16
        if (isset($visited[$oid])) {
2000
            return; // Prevent infinite recursion
2001
        }
2002
2003 16
        $visited[$oid] = $entity; // mark visited
2004
2005 16
        switch ($this->getEntityState($entity, self::STATE_DETACHED)) {
2006 16
            case self::STATE_MANAGED:
2007 14
                if ($this->isInIdentityMap($entity)) {
2008 13
                    $this->removeFromIdentityMap($entity);
2009
                }
2010
2011
                unset(
2012 14
                    $this->entityInsertions[$oid],
2013 14
                    $this->entityUpdates[$oid],
2014 14
                    $this->entityDeletions[$oid],
2015 14
                    $this->entityIdentifiers[$oid],
2016 14
                    $this->entityStates[$oid],
2017 14
                    $this->originalEntityData[$oid]
2018
                );
2019 14
                break;
2020 3
            case self::STATE_NEW:
2021 3
            case self::STATE_DETACHED:
2022 3
                return;
2023
        }
2024
2025 14
        if ( ! $noCascade) {
2026 14
            $this->cascadeDetach($entity, $visited);
2027
        }
2028 14
    }
2029
2030
    /**
2031
     * Refreshes the state of the given entity from the database, overwriting
2032
     * any local, unpersisted changes.
2033
     *
2034
     * @param object $entity The entity to refresh.
2035
     *
2036
     * @return void
2037
     *
2038
     * @throws InvalidArgumentException If the entity is not MANAGED.
2039
     */
2040 17
    public function refresh($entity)
2041
    {
2042 17
        $visited = [];
2043
2044 17
        $this->doRefresh($entity, $visited);
2045 17
    }
2046
2047
    /**
2048
     * Executes a refresh operation on an entity.
2049
     *
2050
     * @param object $entity  The entity to refresh.
2051
     * @param array  $visited The already visited entities during cascades.
2052
     *
2053
     * @return void
2054
     *
2055
     * @throws ORMInvalidArgumentException If the entity is not MANAGED.
2056
     */
2057 17
    private function doRefresh($entity, array &$visited)
2058
    {
2059 17
        $oid = spl_object_hash($entity);
2060
2061 17
        if (isset($visited[$oid])) {
2062
            return; // Prevent infinite recursion
2063
        }
2064
2065 17
        $visited[$oid] = $entity; // mark visited
2066
2067 17
        $class = $this->em->getClassMetadata(get_class($entity));
2068
2069 17
        if ($this->getEntityState($entity) !== self::STATE_MANAGED) {
2070
            throw ORMInvalidArgumentException::entityNotManaged($entity);
2071
        }
2072
2073 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...
2074 17
            array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
2075 17
            $entity
2076
        );
2077
2078 17
        $this->cascadeRefresh($entity, $visited);
2079 17
    }
2080
2081
    /**
2082
     * Cascades a refresh operation to associated entities.
2083
     *
2084
     * @param object $entity
2085
     * @param array  $visited
2086
     *
2087
     * @return void
2088
     */
2089 17 View Code Duplication
    private function cascadeRefresh($entity, array &$visited)
2090
    {
2091 17
        $class = $this->em->getClassMetadata(get_class($entity));
2092
2093 17
        $associationMappings = array_filter(
2094 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...
2095
            function ($assoc) { return $assoc['isCascadeRefresh']; }
2096
        );
2097
2098 17
        foreach ($associationMappings as $assoc) {
2099 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...
2100
2101
            switch (true) {
2102 5
                case ($relatedEntities instanceof PersistentCollection):
2103
                    // Unwrap so that foreach() does not initialize
2104 5
                    $relatedEntities = $relatedEntities->unwrap();
2105
                    // break; is commented intentionally!
2106
2107
                case ($relatedEntities instanceof Collection):
2108
                case (is_array($relatedEntities)):
2109 5
                    foreach ($relatedEntities as $relatedEntity) {
2110
                        $this->doRefresh($relatedEntity, $visited);
2111
                    }
2112 5
                    break;
2113
2114
                case ($relatedEntities !== null):
2115
                    $this->doRefresh($relatedEntities, $visited);
2116
                    break;
2117
2118 5
                default:
2119
                    // Do nothing
2120
            }
2121
        }
2122 17
    }
2123
2124
    /**
2125
     * Cascades a detach operation to associated entities.
2126
     *
2127
     * @param object $entity
2128
     * @param array  $visited
2129
     *
2130
     * @return void
2131
     */
2132 14 View Code Duplication
    private function cascadeDetach($entity, array &$visited)
2133
    {
2134 14
        $class = $this->em->getClassMetadata(get_class($entity));
2135
2136 14
        $associationMappings = array_filter(
2137 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...
2138
            function ($assoc) { return $assoc['isCascadeDetach']; }
2139
        );
2140
2141 14
        foreach ($associationMappings as $assoc) {
2142 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...
2143
2144
            switch (true) {
2145 3
                case ($relatedEntities instanceof PersistentCollection):
2146
                    // Unwrap so that foreach() does not initialize
2147 2
                    $relatedEntities = $relatedEntities->unwrap();
2148
                    // break; is commented intentionally!
2149
2150 1
                case ($relatedEntities instanceof Collection):
2151
                case (is_array($relatedEntities)):
2152 3
                    foreach ($relatedEntities as $relatedEntity) {
2153 1
                        $this->doDetach($relatedEntity, $visited);
2154
                    }
2155 3
                    break;
2156
2157
                case ($relatedEntities !== null):
2158
                    $this->doDetach($relatedEntities, $visited);
2159
                    break;
2160
2161 3
                default:
2162
                    // Do nothing
2163
            }
2164
        }
2165 14
    }
2166
2167
    /**
2168
     * Cascades a merge operation to associated entities.
2169
     *
2170
     * @param object $entity
2171
     * @param object $managedCopy
2172
     * @param array  $visited
2173
     *
2174
     * @return void
2175
     */
2176 41
    private function cascadeMerge($entity, $managedCopy, array &$visited)
2177
    {
2178 41
        $class = $this->em->getClassMetadata(get_class($entity));
2179
2180 41
        $associationMappings = array_filter(
2181 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...
2182
            function ($assoc) { return $assoc['isCascadeMerge']; }
2183
        );
2184
2185 41
        foreach ($associationMappings as $assoc) {
2186 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...
2187
2188 16
            if ($relatedEntities instanceof Collection) {
2189 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...
2190 1
                    continue;
2191
                }
2192
2193 9
                if ($relatedEntities instanceof PersistentCollection) {
2194
                    // Unwrap so that foreach() does not initialize
2195 5
                    $relatedEntities = $relatedEntities->unwrap();
2196
                }
2197
2198 9
                foreach ($relatedEntities as $relatedEntity) {
2199 9
                    $this->doMerge($relatedEntity, $visited, $managedCopy, $assoc);
2200
                }
2201 7
            } else if ($relatedEntities !== null) {
2202 15
                $this->doMerge($relatedEntities, $visited, $managedCopy, $assoc);
2203
            }
2204
        }
2205 41
    }
2206
2207
    /**
2208
     * Cascades the save operation to associated entities.
2209
     *
2210
     * @param object $entity
2211
     * @param array  $visited
2212
     *
2213
     * @return void
2214
     */
2215 1056
    private function cascadePersist($entity, array &$visited)
2216
    {
2217 1056
        $class = $this->em->getClassMetadata(get_class($entity));
2218
2219 1056
        $associationMappings = array_filter(
2220 1056
            $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...
2221
            function ($assoc) { return $assoc['isCascadePersist']; }
2222
        );
2223
2224 1056
        foreach ($associationMappings as $assoc) {
2225 661
            $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...
2226
2227
            switch (true) {
2228 661
                case ($relatedEntities instanceof PersistentCollection):
2229
                    // Unwrap so that foreach() does not initialize
2230 21
                    $relatedEntities = $relatedEntities->unwrap();
2231
                    // break; is commented intentionally!
2232
2233 661
                case ($relatedEntities instanceof Collection):
2234 600
                case (is_array($relatedEntities)):
2235 565
                    if (($assoc['type'] & ClassMetadata::TO_MANY) <= 0) {
2236 3
                        throw ORMInvalidArgumentException::invalidAssociation(
2237 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...
2238 3
                            $assoc,
2239 3
                            $relatedEntities
2240
                        );
2241
                    }
2242
2243 562
                    foreach ($relatedEntities as $relatedEntity) {
2244 283
                        $this->doPersist($relatedEntity, $visited);
2245
                    }
2246
2247 562
                    break;
2248
2249 590
                case ($relatedEntities !== null):
2250 252
                    if (! $relatedEntities instanceof $assoc['targetEntity']) {
2251 4
                        throw ORMInvalidArgumentException::invalidAssociation(
2252 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...
2253 4
                            $assoc,
2254 4
                            $relatedEntities
2255
                        );
2256
                    }
2257
2258 248
                    $this->doPersist($relatedEntities, $visited);
2259 248
                    break;
2260
2261 655
                default:
2262
                    // Do nothing
2263
            }
2264
        }
2265 1049
    }
2266
2267
    /**
2268
     * Cascades the delete operation to associated entities.
2269
     *
2270
     * @param object $entity
2271
     * @param array  $visited
2272
     *
2273
     * @return void
2274
     */
2275 65
    private function cascadeRemove($entity, array &$visited)
2276
    {
2277 65
        $class = $this->em->getClassMetadata(get_class($entity));
2278
2279 65
        $associationMappings = array_filter(
2280 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...
2281
            function ($assoc) { return $assoc['isCascadeRemove']; }
2282
        );
2283
2284 65
        $entitiesToCascade = [];
2285
2286 65
        foreach ($associationMappings as $assoc) {
2287 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...
2288 6
                $entity->__load();
2289
            }
2290
2291 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...
2292
2293
            switch (true) {
2294 26
                case ($relatedEntities instanceof Collection):
2295 19
                case (is_array($relatedEntities)):
2296
                    // If its a PersistentCollection initialization is intended! No unwrap!
2297 20
                    foreach ($relatedEntities as $relatedEntity) {
2298 10
                        $entitiesToCascade[] = $relatedEntity;
2299
                    }
2300 20
                    break;
2301
2302 19
                case ($relatedEntities !== null):
2303 7
                    $entitiesToCascade[] = $relatedEntities;
2304 7
                    break;
2305
2306 26
                default:
2307
                    // Do nothing
2308
            }
2309
        }
2310
2311 65
        foreach ($entitiesToCascade as $relatedEntity) {
2312 16
            $this->doRemove($relatedEntity, $visited);
2313
        }
2314 65
    }
2315
2316
    /**
2317
     * Acquire a lock on the given entity.
2318
     *
2319
     * @param object $entity
2320
     * @param int    $lockMode
2321
     * @param int    $lockVersion
2322
     *
2323
     * @return void
2324
     *
2325
     * @throws ORMInvalidArgumentException
2326
     * @throws TransactionRequiredException
2327
     * @throws OptimisticLockException
2328
     */
2329 10
    public function lock($entity, $lockMode, $lockVersion = null)
2330
    {
2331 10
        if ($entity === null) {
2332 1
            throw new \InvalidArgumentException("No entity passed to UnitOfWork#lock().");
2333
        }
2334
2335 9
        if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) {
2336 1
            throw ORMInvalidArgumentException::entityNotManaged($entity);
2337
        }
2338
2339 8
        $class = $this->em->getClassMetadata(get_class($entity));
2340
2341
        switch (true) {
2342 8
            case LockMode::OPTIMISTIC === $lockMode:
2343 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...
2344 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...
2345
                }
2346
2347 4
                if ($lockVersion === null) {
2348
                    return;
2349
                }
2350
2351 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...
2352 1
                    $entity->__load();
2353
                }
2354
2355 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...
2356
2357 4
                if ($entityVersion != $lockVersion) {
2358 2
                    throw OptimisticLockException::lockFailedVersionMismatch($entity, $lockVersion, $entityVersion);
2359
                }
2360
2361 2
                break;
2362
2363 2
            case LockMode::NONE === $lockMode:
2364 2
            case LockMode::PESSIMISTIC_READ === $lockMode:
2365 1
            case LockMode::PESSIMISTIC_WRITE === $lockMode:
2366 2
                if (!$this->em->getConnection()->isTransactionActive()) {
2367 2
                    throw TransactionRequiredException::transactionRequired();
2368
                }
2369
2370
                $oid = spl_object_hash($entity);
2371
2372
                $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...
2373
                    array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
2374
                    $lockMode
2375
                );
2376
                break;
2377
2378
            default:
2379
                // Do nothing
2380
        }
2381 2
    }
2382
2383
    /**
2384
     * Gets the CommitOrderCalculator used by the UnitOfWork to order commits.
2385
     *
2386
     * @return \Doctrine\ORM\Internal\CommitOrderCalculator
2387
     */
2388 1033
    public function getCommitOrderCalculator()
2389
    {
2390 1033
        return new Internal\CommitOrderCalculator();
2391
    }
2392
2393
    /**
2394
     * Clears the UnitOfWork.
2395
     *
2396
     * @param string|null $entityName if given, only entities of this type will get detached.
2397
     *
2398
     * @return void
2399
     *
2400
     * @throws ORMInvalidArgumentException if an invalid entity name is given
2401
     */
2402 1254
    public function clear($entityName = null)
2403
    {
2404 1254
        if ($entityName === null) {
2405 1252
            $this->identityMap =
2406 1252
            $this->entityIdentifiers =
2407 1252
            $this->originalEntityData =
2408 1252
            $this->entityChangeSets =
2409 1252
            $this->entityStates =
2410 1252
            $this->scheduledForSynchronization =
2411 1252
            $this->entityInsertions =
2412 1252
            $this->entityUpdates =
2413 1252
            $this->entityDeletions =
2414 1252
            $this->collectionDeletions =
2415 1252
            $this->collectionUpdates =
2416 1252
            $this->extraUpdates =
2417 1252
            $this->readOnlyObjects =
2418 1252
            $this->visitedCollections =
2419 1252
            $this->orphanRemovals = [];
2420
        } else {
2421 4
            $this->clearIdentityMapForEntityName($entityName);
2422 4
            $this->clearEntityInsertionsForEntityName($entityName);
2423
        }
2424
2425 1254
        if ($this->evm->hasListeners(Events::onClear)) {
2426 9
            $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em, $entityName));
2427
        }
2428 1254
    }
2429
2430
    /**
2431
     * INTERNAL:
2432
     * Schedules an orphaned entity for removal. The remove() operation will be
2433
     * invoked on that entity at the beginning of the next commit of this
2434
     * UnitOfWork.
2435
     *
2436
     * @ignore
2437
     *
2438
     * @param object $entity
2439
     *
2440
     * @return void
2441
     */
2442 17
    public function scheduleOrphanRemoval($entity)
2443
    {
2444 17
        $this->orphanRemovals[spl_object_hash($entity)] = $entity;
2445 17
    }
2446
2447
    /**
2448
     * INTERNAL:
2449
     * Cancels a previously scheduled orphan removal.
2450
     *
2451
     * @ignore
2452
     *
2453
     * @param object $entity
2454
     *
2455
     * @return void
2456
     */
2457 117
    public function cancelOrphanRemoval($entity)
2458
    {
2459 117
        unset($this->orphanRemovals[spl_object_hash($entity)]);
2460 117
    }
2461
2462
    /**
2463
     * INTERNAL:
2464
     * Schedules a complete collection for removal when this UnitOfWork commits.
2465
     *
2466
     * @param PersistentCollection $coll
2467
     *
2468
     * @return void
2469
     */
2470 14
    public function scheduleCollectionDeletion(PersistentCollection $coll)
2471
    {
2472 14
        $coid = spl_object_hash($coll);
2473
2474
        // TODO: if $coll is already scheduled for recreation ... what to do?
2475
        // Just remove $coll from the scheduled recreations?
2476 14
        unset($this->collectionUpdates[$coid]);
2477
2478 14
        $this->collectionDeletions[$coid] = $coll;
2479 14
    }
2480
2481
    /**
2482
     * @param PersistentCollection $coll
2483
     *
2484
     * @return bool
2485
     */
2486
    public function isCollectionScheduledForDeletion(PersistentCollection $coll)
2487
    {
2488
        return isset($this->collectionDeletions[spl_object_hash($coll)]);
2489
    }
2490
2491
    /**
2492
     * @param ClassMetadata $class
2493
     *
2494
     * @return \Doctrine\Common\Persistence\ObjectManagerAware|object
2495
     */
2496 697
    private function newInstance($class)
2497
    {
2498 697
        $entity = $class->newInstance();
2499
2500 697
        if ($entity instanceof \Doctrine\Common\Persistence\ObjectManagerAware) {
2501 4
            $entity->injectObjectManager($this->em, $class);
2502
        }
2503
2504 697
        return $entity;
2505
    }
2506
2507
    /**
2508
     * INTERNAL:
2509
     * Creates an entity. Used for reconstitution of persistent entities.
2510
     *
2511
     * Internal note: Highly performance-sensitive method.
2512
     *
2513
     * @ignore
2514
     *
2515
     * @param string $className The name of the entity class.
2516
     * @param array  $data      The data for the entity.
2517
     * @param array  $hints     Any hints to account for during reconstitution/lookup of the entity.
2518
     *
2519
     * @return object The managed entity instance.
2520
     *
2521
     * @todo Rename: getOrCreateEntity
2522
     */
2523 834
    public function createEntity($className, array $data, &$hints = [])
2524
    {
2525 834
        $class = $this->em->getClassMetadata($className);
2526
        //$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...
2527
2528 834
        $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...
2529 834
        $idHash = implode(' ', $id);
2530
2531 834
        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...
2532 318
            $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...
2533 318
            $oid = spl_object_hash($entity);
2534
2535
            if (
2536 318
                isset($hints[Query::HINT_REFRESH])
2537 318
                && isset($hints[Query::HINT_REFRESH_ENTITY])
2538 318
                && ($unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY]) !== $entity
2539 318
                && $unmanagedProxy instanceof Proxy
2540 318
                && $this->isIdentifierEquals($unmanagedProxy, $entity)
2541
            ) {
2542
                // DDC-1238 - we have a managed instance, but it isn't the provided one.
2543
                // Therefore we clear its identifier. Also, we must re-fetch metadata since the
2544
                // refreshed object may be anything
2545
2546 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...
2547 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...
2548
                }
2549
2550 2
                return $unmanagedProxy;
2551
            }
2552
2553 316
            if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
2554 23
                $entity->__setInitialized(true);
2555
2556 23
                $overrideLocalValues = true;
2557
2558 23
                if ($entity instanceof NotifyPropertyChanged) {
2559 23
                    $entity->addPropertyChangedListener($this);
2560
                }
2561
            } else {
2562 295
                $overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
2563
2564
                // If only a specific entity is set to refresh, check that it's the one
2565 295
                if (isset($hints[Query::HINT_REFRESH_ENTITY])) {
2566 72
                    $overrideLocalValues = $hints[Query::HINT_REFRESH_ENTITY] === $entity;
2567
                }
2568
            }
2569
2570 316
            if ($overrideLocalValues) {
2571
                // inject ObjectManager upon refresh.
2572 115
                if ($entity instanceof ObjectManagerAware) {
2573 3
                    $entity->injectObjectManager($this->em, $class);
2574
                }
2575
2576 316
                $this->originalEntityData[$oid] = $data;
2577
            }
2578
        } else {
2579 692
            $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...
2580 692
            $oid    = spl_object_hash($entity);
2581
2582 692
            $this->entityIdentifiers[$oid]  = $id;
2583 692
            $this->entityStates[$oid]       = self::STATE_MANAGED;
2584 692
            $this->originalEntityData[$oid] = $data;
2585
2586 692
            $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...
2587
2588 692
            if ($entity instanceof NotifyPropertyChanged) {
2589 2
                $entity->addPropertyChangedListener($this);
2590
            }
2591
2592 692
            $overrideLocalValues = true;
2593
        }
2594
2595 833
        if ( ! $overrideLocalValues) {
2596 224
            return $entity;
2597
        }
2598
2599 730
        foreach ($data as $field => $value) {
2600 730
            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...
2601 730
                $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...
2602
            }
2603
        }
2604
2605
        // Loading the entity right here, if its in the eager loading map get rid of it there.
2606 730
        unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
2607
2608 730
        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...
2609
            unset($this->eagerLoadingEntities[$class->rootEntityName]);
2610
        }
2611
2612
        // Properly initialize any unfetched associations, if partial objects are not allowed.
2613 730
        if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
2614 34
            return $entity;
2615
        }
2616
2617 696
        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...
2618
            // Check if the association is not among the fetch-joined associations already.
2619 602
            if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) {
2620 258
                continue;
2621
            }
2622
2623 578
            $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
2624
2625
            switch (true) {
2626 578
                case ($assoc['type'] & ClassMetadata::TO_ONE):
2627 498
                    if ( ! $assoc['isOwningSide']) {
2628
2629
                        // use the given entity association
2630 67
                        if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2631
2632 3
                            $this->originalEntityData[$oid][$field] = $data[$field];
2633
2634 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...
2635 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...
2636
2637 3
                            continue 2;
2638
                        }
2639
2640
                        // Inverse side of x-to-one can never be lazy
2641 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...
2642
2643 64
                        continue 2;
2644
                    }
2645
2646
                    // use the entity association
2647 498
                    if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2648 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...
2649 38
                        $this->originalEntityData[$oid][$field] = $data[$field];
2650
2651 38
                        continue;
2652
                    }
2653
2654 491
                    $associatedId = [];
2655
2656
                    // TODO: Is this even computed right in all cases of composite keys?
2657 491
                    foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
2658 491
                        $joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
2659
2660 491
                        if ($joinColumnValue !== null) {
2661 296
                            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...
2662 11
                                $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
2663
                            } else {
2664 296
                                $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...
2665
                            }
2666 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...
2667 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...
2668
                        ) {
2669
                            // the missing key is part of target's entity primary key
2670 7
                            $associatedId = [];
2671 491
                            break;
2672
                        }
2673
                    }
2674
2675 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...
2676
                        // Foreign key is NULL
2677 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...
2678 289
                        $this->originalEntityData[$oid][$field] = null;
2679
2680 289
                        continue;
2681
                    }
2682
2683 296
                    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...
2684 293
                        $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...
2685
                    }
2686
2687
                    // Foreign key is set
2688
                    // Check identity map first
2689
                    // FIXME: Can break easily with composite keys if join column values are in
2690
                    //        wrong order. The correct order is the one in ClassMetadata#identifier.
2691 296
                    $relatedIdHash = implode(' ', $associatedId);
2692
2693
                    switch (true) {
2694 296
                        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...
2695 171
                            $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...
2696
2697
                            // If this is an uninitialized proxy, we are deferring eager loads,
2698
                            // this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
2699
                            // then we can append this entity for eager loading!
2700 171
                            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...
2701 171
                                isset($hints[self::HINT_DEFEREAGERLOAD]) &&
2702 171
                                !$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...
2703 171
                                $newValue instanceof Proxy &&
2704 171
                                $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...
2705
2706
                                $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...
2707
                            }
2708
2709 171
                            break;
2710
2711 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...
2712
                            // If it might be a subtype, it can not be lazy. There isn't even
2713
                            // a way to solve this with deferred eager loading, which means putting
2714
                            // an entity with subclasses at a *-to-one location is really bad! (performance-wise)
2715 32
                            $newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, $associatedId);
2716 32
                            break;
2717
2718
                        default:
2719
                            switch (true) {
2720
                                // We are negating the condition here. Other cases will assume it is valid!
2721 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...
2722 164
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2723 164
                                    break;
2724
2725
                                // Deferred eager load only works for single identifier classes
2726 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...
2727
                                    // TODO: Is there a faster approach?
2728 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...
2729
2730 7
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2731 7
                                    break;
2732
2733
                                default:
2734
                                    // TODO: This is very imperformant, ignore it?
2735
                                    $newValue = $this->em->find($assoc['targetEntity'], $associatedId);
2736
                                    break;
2737
                            }
2738
2739
                            // PERF: Inlined & optimized code from UnitOfWork#registerManaged()
2740 171
                            $newValueOid = spl_object_hash($newValue);
2741 171
                            $this->entityIdentifiers[$newValueOid] = $associatedId;
2742 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...
2743
2744
                            if (
2745 171
                                $newValue instanceof NotifyPropertyChanged &&
2746 171
                                ( ! $newValue instanceof Proxy || $newValue->__isInitialized())
2747
                            ) {
2748
                                $newValue->addPropertyChangedListener($this);
2749
                            }
2750 171
                            $this->entityStates[$newValueOid] = self::STATE_MANAGED;
2751
                            // make sure that when an proxy is then finally loaded, $this->originalEntityData is set also!
2752 171
                            break;
2753
                    }
2754
2755 296
                    $this->originalEntityData[$oid][$field] = $newValue;
2756 296
                    $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...
2757
2758 296
                    if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
2759 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...
2760 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...
2761
                    }
2762
2763 296
                    break;
2764
2765
                default:
2766
                    // Ignore if its a cached collection
2767 492
                    if (isset($hints[Query::HINT_CACHE_ENABLED]) && $class->getFieldValue($entity, $field) instanceof PersistentCollection) {
2768
                        break;
2769
                    }
2770
2771
                    // use the given collection
2772 492
                    if (isset($data[$field]) && $data[$field] instanceof PersistentCollection) {
2773
2774 3
                        $data[$field]->setOwner($entity, $assoc);
2775
2776 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...
2777 3
                        $this->originalEntityData[$oid][$field] = $data[$field];
2778
2779 3
                        break;
2780
                    }
2781
2782
                    // Inject collection
2783 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...
2784 492
                    $pColl->setOwner($entity, $assoc);
2785 492
                    $pColl->setInitialized(false);
2786
2787 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...
2788 492
                    $reflField->setValue($entity, $pColl);
2789
2790 492
                    if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
2791 4
                        $this->loadCollection($pColl);
2792 4
                        $pColl->takeSnapshot();
2793
                    }
2794
2795 492
                    $this->originalEntityData[$oid][$field] = $pColl;
2796 578
                    break;
2797
            }
2798
        }
2799
2800 696
        if ($overrideLocalValues) {
2801
            // defer invoking of postLoad event to hydration complete step
2802 696
            $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...
2803
        }
2804
2805 696
        return $entity;
2806
    }
2807
2808
    /**
2809
     * @return void
2810
     */
2811 899
    public function triggerEagerLoads()
2812
    {
2813 899
        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...
2814 899
            return;
2815
        }
2816
2817
        // avoid infinite recursion
2818 7
        $eagerLoadingEntities       = $this->eagerLoadingEntities;
2819 7
        $this->eagerLoadingEntities = [];
2820
2821 7
        foreach ($eagerLoadingEntities as $entityName => $ids) {
2822 7
            if ( ! $ids) {
2823
                continue;
2824
            }
2825
2826 7
            $class = $this->em->getClassMetadata($entityName);
2827
2828 7
            $this->getEntityPersister($entityName)->loadAll(
2829 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...
2830
            );
2831
        }
2832 7
    }
2833
2834
    /**
2835
     * Initializes (loads) an uninitialized persistent collection of an entity.
2836
     *
2837
     * @param \Doctrine\ORM\PersistentCollection $collection The collection to initialize.
2838
     *
2839
     * @return void
2840
     *
2841
     * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733.
2842
     */
2843 147
    public function loadCollection(PersistentCollection $collection)
2844
    {
2845 147
        $assoc     = $collection->getMapping();
2846 147
        $persister = $this->getEntityPersister($assoc['targetEntity']);
2847
2848 147
        switch ($assoc['type']) {
2849 147
            case ClassMetadata::ONE_TO_MANY:
2850 77
                $persister->loadOneToManyCollection($assoc, $collection->getOwner(), $collection);
2851 77
                break;
2852
2853 84
            case ClassMetadata::MANY_TO_MANY:
2854 84
                $persister->loadManyToManyCollection($assoc, $collection->getOwner(), $collection);
2855 84
                break;
2856
        }
2857
2858 147
        $collection->setInitialized(true);
2859 147
    }
2860
2861
    /**
2862
     * Gets the identity map of the UnitOfWork.
2863
     *
2864
     * @return array
2865
     */
2866 2
    public function getIdentityMap()
2867
    {
2868 2
        return $this->identityMap;
2869
    }
2870
2871
    /**
2872
     * Gets the original data of an entity. The original data is the data that was
2873
     * present at the time the entity was reconstituted from the database.
2874
     *
2875
     * @param object $entity
2876
     *
2877
     * @return array
2878
     */
2879 120
    public function getOriginalEntityData($entity)
2880
    {
2881 120
        $oid = spl_object_hash($entity);
2882
2883 120
        return isset($this->originalEntityData[$oid])
2884 116
            ? $this->originalEntityData[$oid]
2885 120
            : [];
2886
    }
2887
2888
    /**
2889
     * @ignore
2890
     *
2891
     * @param object $entity
2892
     * @param array  $data
2893
     *
2894
     * @return void
2895
     */
2896
    public function setOriginalEntityData($entity, array $data)
2897
    {
2898
        $this->originalEntityData[spl_object_hash($entity)] = $data;
2899
    }
2900
2901
    /**
2902
     * INTERNAL:
2903
     * Sets a property value of the original data array of an entity.
2904
     *
2905
     * @ignore
2906
     *
2907
     * @param string $oid
2908
     * @param string $property
2909
     * @param mixed  $value
2910
     *
2911
     * @return void
2912
     */
2913 312
    public function setOriginalEntityProperty($oid, $property, $value)
2914
    {
2915 312
        $this->originalEntityData[$oid][$property] = $value;
2916 312
    }
2917
2918
    /**
2919
     * Gets the identifier of an entity.
2920
     * The returned value is always an array of identifier values. If the entity
2921
     * has a composite identifier then the identifier values are in the same
2922
     * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames().
2923
     *
2924
     * @param object $entity
2925
     *
2926
     * @return array The identifier values.
2927
     */
2928 858
    public function getEntityIdentifier($entity)
2929
    {
2930 858
        return $this->entityIdentifiers[spl_object_hash($entity)];
2931
    }
2932
2933
    /**
2934
     * Processes an entity instance to extract their identifier values.
2935
     *
2936
     * @param object $entity The entity instance.
2937
     *
2938
     * @return mixed A scalar value.
2939
     *
2940
     * @throws \Doctrine\ORM\ORMInvalidArgumentException
2941
     */
2942 127
    public function getSingleIdentifierValue($entity)
2943
    {
2944 127
        $class = $this->em->getClassMetadata(get_class($entity));
2945
2946 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...
2947
            throw ORMInvalidArgumentException::invalidCompositeIdentifier();
2948
        }
2949
2950 127
        $values = $this->isInIdentityMap($entity)
2951 114
            ? $this->getEntityIdentifier($entity)
2952 127
            : $class->getIdentifierValues($entity);
2953
2954 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...
2955
    }
2956
2957
    /**
2958
     * Tries to find an entity with the given identifier in the identity map of
2959
     * this UnitOfWork.
2960
     *
2961
     * @param mixed  $id            The entity identifier to look for.
2962
     * @param string $rootClassName The name of the root class of the mapped entity hierarchy.
2963
     *
2964
     * @return object|bool Returns the entity with the specified identifier if it exists in
2965
     *                     this UnitOfWork, FALSE otherwise.
2966
     */
2967 545 View Code Duplication
    public function tryGetById($id, $rootClassName)
2968
    {
2969 545
        $idHash = implode(' ', (array) $id);
2970
2971 545
        return isset($this->identityMap[$rootClassName][$idHash])
2972 85
            ? $this->identityMap[$rootClassName][$idHash]
2973 545
            : false;
2974
    }
2975
2976
    /**
2977
     * Schedules an entity for dirty-checking at commit-time.
2978
     *
2979
     * @param object $entity The entity to schedule for dirty-checking.
2980
     *
2981
     * @return void
2982
     *
2983
     * @todo Rename: scheduleForSynchronization
2984
     */
2985 5
    public function scheduleForDirtyCheck($entity)
2986
    {
2987 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...
2988
2989 5
        $this->scheduledForSynchronization[$rootClassName][spl_object_hash($entity)] = $entity;
2990 5
    }
2991
2992
    /**
2993
     * Checks whether the UnitOfWork has any pending insertions.
2994
     *
2995
     * @return boolean TRUE if this UnitOfWork has pending insertions, FALSE otherwise.
2996
     */
2997
    public function hasPendingInsertions()
2998
    {
2999
        return ! empty($this->entityInsertions);
3000
    }
3001
3002
    /**
3003
     * Calculates the size of the UnitOfWork. The size of the UnitOfWork is the
3004
     * number of entities in the identity map.
3005
     *
3006
     * @return integer
3007
     */
3008 1
    public function size()
3009
    {
3010 1
        $countArray = array_map('count', $this->identityMap);
3011
3012 1
        return array_sum($countArray);
3013
    }
3014
3015
    /**
3016
     * Gets the EntityPersister for an Entity.
3017
     *
3018
     * @param string $entityName The name of the Entity.
3019
     *
3020
     * @return \Doctrine\ORM\Persisters\Entity\EntityPersister
3021
     */
3022 1101
    public function getEntityPersister($entityName)
3023
    {
3024 1101
        if (isset($this->persisters[$entityName])) {
3025 864
            return $this->persisters[$entityName];
3026
        }
3027
3028 1101
        $class = $this->em->getClassMetadata($entityName);
3029
3030
        switch (true) {
3031 1101
            case ($class->isInheritanceTypeNone()):
3032 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...
3033 1062
                break;
3034
3035 372
            case ($class->isInheritanceTypeSingleTable()):
3036 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...
3037 225
                break;
3038
3039 340
            case ($class->isInheritanceTypeJoined()):
3040 340
                $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...
3041 340
                break;
3042
3043
            default:
3044
                throw new \RuntimeException('No persister found for entity.');
3045
        }
3046
3047 1101
        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...
3048 124
            $persister = $this->em->getConfiguration()
3049 124
                ->getSecondLevelCacheConfiguration()
3050 124
                ->getCacheFactory()
3051 124
                ->buildCachedEntityPersister($this->em, $persister, $class);
3052
        }
3053
3054 1101
        $this->persisters[$entityName] = $persister;
3055
3056 1101
        return $this->persisters[$entityName];
3057
    }
3058
3059
    /**
3060
     * Gets a collection persister for a collection-valued association.
3061
     *
3062
     * @param array $association
3063
     *
3064
     * @return \Doctrine\ORM\Persisters\Collection\CollectionPersister
3065
     */
3066 574
    public function getCollectionPersister(array $association)
3067
    {
3068 574
        $role = isset($association['cache'])
3069 78
            ? $association['sourceEntity'] . '::' . $association['fieldName']
3070 574
            : $association['type'];
3071
3072 574
        if (isset($this->collectionPersisters[$role])) {
3073 452
            return $this->collectionPersisters[$role];
3074
        }
3075
3076 574
        $persister = ClassMetadata::ONE_TO_MANY === $association['type']
3077 408
            ? new OneToManyPersister($this->em)
3078 574
            : new ManyToManyPersister($this->em);
3079
3080 574
        if ($this->hasCache && isset($association['cache'])) {
3081 77
            $persister = $this->em->getConfiguration()
3082 77
                ->getSecondLevelCacheConfiguration()
3083 77
                ->getCacheFactory()
3084 77
                ->buildCachedCollectionPersister($this->em, $persister, $association);
3085
        }
3086
3087 574
        $this->collectionPersisters[$role] = $persister;
3088
3089 574
        return $this->collectionPersisters[$role];
3090
    }
3091
3092
    /**
3093
     * INTERNAL:
3094
     * Registers an entity as managed.
3095
     *
3096
     * @param object $entity The entity.
3097
     * @param array  $id     The identifier values.
3098
     * @param array  $data   The original entity data.
3099
     *
3100
     * @return void
3101
     */
3102 209
    public function registerManaged($entity, array $id, array $data)
3103
    {
3104 209
        $oid = spl_object_hash($entity);
3105
3106 209
        $this->entityIdentifiers[$oid]  = $id;
3107 209
        $this->entityStates[$oid]       = self::STATE_MANAGED;
3108 209
        $this->originalEntityData[$oid] = $data;
3109
3110 209
        $this->addToIdentityMap($entity);
3111
3112 203
        if ($entity instanceof NotifyPropertyChanged && ( ! $entity instanceof Proxy || $entity->__isInitialized())) {
3113 2
            $entity->addPropertyChangedListener($this);
3114
        }
3115 203
    }
3116
3117
    /**
3118
     * INTERNAL:
3119
     * Clears the property changeset of the entity with the given OID.
3120
     *
3121
     * @param string $oid The entity's OID.
3122
     *
3123
     * @return void
3124
     */
3125 15
    public function clearEntityChangeSet($oid)
3126
    {
3127 15
        unset($this->entityChangeSets[$oid]);
3128 15
    }
3129
3130
    /* PropertyChangedListener implementation */
3131
3132
    /**
3133
     * Notifies this UnitOfWork of a property change in an entity.
3134
     *
3135
     * @param object $entity       The entity that owns the property.
3136
     * @param string $propertyName The name of the property that changed.
3137
     * @param mixed  $oldValue     The old value of the property.
3138
     * @param mixed  $newValue     The new value of the property.
3139
     *
3140
     * @return void
3141
     */
3142 3
    public function propertyChanged($entity, $propertyName, $oldValue, $newValue)
3143
    {
3144 3
        $oid   = spl_object_hash($entity);
3145 3
        $class = $this->em->getClassMetadata(get_class($entity));
3146
3147 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...
3148
3149 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...
3150 1
            return; // ignore non-persistent fields
3151
        }
3152
3153
        // Update changeset and mark entity for synchronization
3154 3
        $this->entityChangeSets[$oid][$propertyName] = [$oldValue, $newValue];
3155
3156 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...
3157 3
            $this->scheduleForDirtyCheck($entity);
3158
        }
3159 3
    }
3160
3161
    /**
3162
     * Gets the currently scheduled entity insertions in this UnitOfWork.
3163
     *
3164
     * @return array
3165
     */
3166 2
    public function getScheduledEntityInsertions()
3167
    {
3168 2
        return $this->entityInsertions;
3169
    }
3170
3171
    /**
3172
     * Gets the currently scheduled entity updates in this UnitOfWork.
3173
     *
3174
     * @return array
3175
     */
3176 3
    public function getScheduledEntityUpdates()
3177
    {
3178 3
        return $this->entityUpdates;
3179
    }
3180
3181
    /**
3182
     * Gets the currently scheduled entity deletions in this UnitOfWork.
3183
     *
3184
     * @return array
3185
     */
3186 1
    public function getScheduledEntityDeletions()
3187
    {
3188 1
        return $this->entityDeletions;
3189
    }
3190
3191
    /**
3192
     * Gets the currently scheduled complete collection deletions
3193
     *
3194
     * @return array
3195
     */
3196 1
    public function getScheduledCollectionDeletions()
3197
    {
3198 1
        return $this->collectionDeletions;
3199
    }
3200
3201
    /**
3202
     * Gets the currently scheduled collection inserts, updates and deletes.
3203
     *
3204
     * @return array
3205
     */
3206
    public function getScheduledCollectionUpdates()
3207
    {
3208
        return $this->collectionUpdates;
3209
    }
3210
3211
    /**
3212
     * Helper method to initialize a lazy loading proxy or persistent collection.
3213
     *
3214
     * @param object $obj
3215
     *
3216
     * @return void
3217
     */
3218 2
    public function initializeObject($obj)
3219
    {
3220 2
        if ($obj instanceof Proxy) {
3221 1
            $obj->__load();
3222
3223 1
            return;
3224
        }
3225
3226 1
        if ($obj instanceof PersistentCollection) {
3227 1
            $obj->initialize();
3228
        }
3229 1
    }
3230
3231
    /**
3232
     * Helper method to show an object as string.
3233
     *
3234
     * @param object $obj
3235
     *
3236
     * @return string
3237
     */
3238 1
    private static function objToStr($obj)
3239
    {
3240 1
        return method_exists($obj, '__toString') ? (string) $obj : get_class($obj).'@'.spl_object_hash($obj);
3241
    }
3242
3243
    /**
3244
     * Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit().
3245
     *
3246
     * This operation cannot be undone as some parts of the UnitOfWork now keep gathering information
3247
     * on this object that might be necessary to perform a correct update.
3248
     *
3249
     * @param object $object
3250
     *
3251
     * @return void
3252
     *
3253
     * @throws ORMInvalidArgumentException
3254
     */
3255 6
    public function markReadOnly($object)
3256
    {
3257 6
        if ( ! is_object($object) || ! $this->isInIdentityMap($object)) {
3258 1
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3259
        }
3260
3261 5
        $this->readOnlyObjects[spl_object_hash($object)] = true;
3262 5
    }
3263
3264
    /**
3265
     * Is this entity read only?
3266
     *
3267
     * @param object $object
3268
     *
3269
     * @return bool
3270
     *
3271
     * @throws ORMInvalidArgumentException
3272
     */
3273 3
    public function isReadOnly($object)
3274
    {
3275 3
        if ( ! is_object($object)) {
3276
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3277
        }
3278
3279 3
        return isset($this->readOnlyObjects[spl_object_hash($object)]);
3280
    }
3281
3282
    /**
3283
     * Perform whatever processing is encapsulated here after completion of the transaction.
3284
     */
3285
    private function afterTransactionComplete()
3286
    {
3287 1028
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3288 93
            $persister->afterTransactionComplete();
3289 1028
        });
3290 1028
    }
3291
3292
    /**
3293
     * Perform whatever processing is encapsulated here after completion of the rolled-back.
3294
     */
3295
    private function afterTransactionRolledBack()
3296
    {
3297 11
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3298 3
            $persister->afterTransactionRolledBack();
3299 11
        });
3300 11
    }
3301
3302
    /**
3303
     * Performs an action after the transaction.
3304
     *
3305
     * @param callable $callback
3306
     */
3307 1033
    private function performCallbackOnCachedPersister(callable $callback)
3308
    {
3309 1033
        if ( ! $this->hasCache) {
3310 940
            return;
3311
        }
3312
3313 93
        foreach (array_merge($this->persisters, $this->collectionPersisters) as $persister) {
3314 93
            if ($persister instanceof CachedPersister) {
3315 93
                $callback($persister);
3316
            }
3317
        }
3318 93
    }
3319
3320 1037
    private function dispatchOnFlushEvent()
3321
    {
3322 1037
        if ($this->evm->hasListeners(Events::onFlush)) {
3323 4
            $this->evm->dispatchEvent(Events::onFlush, new OnFlushEventArgs($this->em));
3324
        }
3325 1037
    }
3326
3327 1032
    private function dispatchPostFlushEvent()
3328
    {
3329 1032
        if ($this->evm->hasListeners(Events::postFlush)) {
3330 5
            $this->evm->dispatchEvent(Events::postFlush, new PostFlushEventArgs($this->em));
3331
        }
3332 1031
    }
3333
3334
    /**
3335
     * Verifies if two given entities actually are the same based on identifier comparison
3336
     *
3337
     * @param object $entity1
3338
     * @param object $entity2
3339
     *
3340
     * @return bool
3341
     */
3342 14
    private function isIdentifierEquals($entity1, $entity2)
3343
    {
3344 14
        if ($entity1 === $entity2) {
3345
            return true;
3346
        }
3347
3348 14
        $class = $this->em->getClassMetadata(get_class($entity1));
3349
3350 14
        if ($class !== $this->em->getClassMetadata(get_class($entity2))) {
3351 11
            return false;
3352
        }
3353
3354 3
        $oid1 = spl_object_hash($entity1);
3355 3
        $oid2 = spl_object_hash($entity2);
3356
3357 3
        $id1 = isset($this->entityIdentifiers[$oid1])
3358 3
            ? $this->entityIdentifiers[$oid1]
3359 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...
3360 3
        $id2 = isset($this->entityIdentifiers[$oid2])
3361 3
            ? $this->entityIdentifiers[$oid2]
3362 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...
3363
3364 3
        return $id1 === $id2 || implode(' ', $id1) === implode(' ', $id2);
3365
    }
3366
3367
    /**
3368
     * @param object $entity
3369
     * @param object $managedCopy
3370
     *
3371
     * @throws ORMException
3372
     * @throws OptimisticLockException
3373
     * @throws TransactionRequiredException
3374
     */
3375 40
    private function mergeEntityStateIntoManagedCopy($entity, $managedCopy)
3376
    {
3377 40
        if (! $this->isLoaded($entity)) {
3378 7
            return;
3379
        }
3380
3381 33
        if (! $this->isLoaded($managedCopy)) {
3382 4
            $managedCopy->__load();
3383
        }
3384
3385 33
        $class = $this->em->getClassMetadata(get_class($entity));
3386
3387 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...
3388 33
            $name = $prop->name;
3389
3390 33
            $prop->setAccessible(true);
3391
3392 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...
3393 33
                if ( ! $class->isIdentifier($name)) {
3394 33
                    $prop->setValue($managedCopy, $prop->getValue($entity));
3395
                }
3396
            } else {
3397 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...
3398
3399 29
                if ($assoc2['type'] & ClassMetadata::TO_ONE) {
3400 25
                    $other = $prop->getValue($entity);
3401 25
                    if ($other === null) {
3402 12
                        $prop->setValue($managedCopy, null);
3403
                    } else {
3404 16
                        if ($other instanceof Proxy && !$other->__isInitialized()) {
3405
                            // do not merge fields marked lazy that have not been fetched.
3406 4
                            continue;
3407
                        }
3408
3409 12
                        if ( ! $assoc2['isCascadeMerge']) {
3410 6
                            if ($this->getEntityState($other) === self::STATE_DETACHED) {
3411 3
                                $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']);
3412 3
                                $relatedId   = $targetClass->getIdentifierValues($other);
3413
3414 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...
3415 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...
3416
                                } else {
3417 1
                                    $other = $this->em->getProxyFactory()->getProxy(
3418 1
                                        $assoc2['targetEntity'],
3419 1
                                        $relatedId
3420
                                    );
3421 1
                                    $this->registerManaged($other, $relatedId, []);
3422
                                }
3423
                            }
3424
3425 21
                            $prop->setValue($managedCopy, $other);
3426
                        }
3427
                    }
3428
                } else {
3429 17
                    $mergeCol = $prop->getValue($entity);
3430
3431 17
                    if ($mergeCol instanceof PersistentCollection && ! $mergeCol->isInitialized()) {
3432
                        // do not merge fields marked lazy that have not been fetched.
3433
                        // keep the lazy persistent collection of the managed copy.
3434 5
                        continue;
3435
                    }
3436
3437 14
                    $managedCol = $prop->getValue($managedCopy);
3438
3439 14
                    if ( ! $managedCol) {
3440 4
                        $managedCol = new PersistentCollection(
3441 4
                            $this->em,
3442 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...
3443 4
                            new ArrayCollection
3444
                        );
3445 4
                        $managedCol->setOwner($managedCopy, $assoc2);
3446 4
                        $prop->setValue($managedCopy, $managedCol);
3447
                    }
3448
3449 14
                    if ($assoc2['isCascadeMerge']) {
3450 9
                        $managedCol->initialize();
3451
3452
                        // clear and set dirty a managed collection if its not also the same collection to merge from.
3453 9
                        if ( ! $managedCol->isEmpty() && $managedCol !== $mergeCol) {
3454 1
                            $managedCol->unwrap()->clear();
3455 1
                            $managedCol->setDirty(true);
3456
3457 1
                            if ($assoc2['isOwningSide']
3458 1
                                && $assoc2['type'] == ClassMetadata::MANY_TO_MANY
3459 1
                                && $class->isChangeTrackingNotify()
3460
                            ) {
3461
                                $this->scheduleForDirtyCheck($managedCopy);
3462
                            }
3463
                        }
3464
                    }
3465
                }
3466
            }
3467
3468 33
            if ($class->isChangeTrackingNotify()) {
3469
                // Just treat all properties as changed, there is no other choice.
3470 33
                $this->propertyChanged($managedCopy, $name, null, $prop->getValue($managedCopy));
3471
            }
3472
        }
3473 33
    }
3474
3475
    /**
3476
     * This method called by hydrators, and indicates that hydrator totally completed current hydration cycle.
3477
     * Unit of work able to fire deferred events, related to loading events here.
3478
     *
3479
     * @internal should be called internally from object hydrators
3480
     */
3481 913
    public function hydrationComplete()
3482
    {
3483 913
        $this->hydrationCompleteHandler->hydrationComplete();
3484 913
    }
3485
3486
    /**
3487
     * @param string $entityName
3488
     */
3489 4
    private function clearIdentityMapForEntityName($entityName)
3490
    {
3491 4
        if (! isset($this->identityMap[$entityName])) {
3492
            return;
3493
        }
3494
3495 4
        $visited = [];
3496
3497 4
        foreach ($this->identityMap[$entityName] as $entity) {
3498 4
            $this->doDetach($entity, $visited, false);
3499
        }
3500 4
    }
3501
3502
    /**
3503
     * @param string $entityName
3504
     */
3505 4
    private function clearEntityInsertionsForEntityName($entityName)
3506
    {
3507 4
        foreach ($this->entityInsertions as $hash => $entity) {
3508
            // note: performance optimization - `instanceof` is much faster than a function call
3509 1
            if ($entity instanceof $entityName && get_class($entity) === $entityName) {
3510 1
                unset($this->entityInsertions[$hash]);
3511
            }
3512
        }
3513 4
    }
3514
3515
    /**
3516
     * @param ClassMetadata $class
3517
     * @param mixed         $identifierValue
3518
     *
3519
     * @return mixed the identifier after type conversion
3520
     *
3521
     * @throws \Doctrine\ORM\Mapping\MappingException if the entity has more than a single identifier
3522
     */
3523 940
    private function convertSingleFieldIdentifierToPHPValue(ClassMetadata $class, $identifierValue)
3524
    {
3525 940
        return $this->em->getConnection()->convertToPHPValue(
3526 940
            $identifierValue,
3527 940
            $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...
3528
        );
3529
    }
3530
}
3531