Failed Conditions
Pull Request — master (#6649)
by Marco
62:39
created

UnitOfWork::createEntity()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

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

An additional type check may prevent trouble.

Loading history...
433 1038
    }
434
435
    /**
436
     * @param null|object|object[] $entity
437
     */
438 1038
    private function postCommitCleanup($entity) : void
439
    {
440 1038
        $this->entityInsertions =
441 1038
        $this->entityUpdates =
442 1038
        $this->entityDeletions =
443 1038
        $this->extraUpdates =
444 1038
        $this->collectionUpdates =
445 1038
        $this->nonCascadedNewDetectedEntities =
446 1038
        $this->collectionDeletions =
447 1038
        $this->visitedCollections =
448 1038
        $this->orphanRemovals = [];
449
450 1038
        if (null === $entity) {
451 1029
            $this->entityChangeSets = $this->scheduledForSynchronization = [];
452
453 1029
            return;
454
        }
455
456 15
        $entities = \is_object($entity)
457 13
            ? [$entity]
458 15
            : $entity;
459
460 15
        foreach ($entities as $object) {
461 15
            $oid = \spl_object_hash($object);
462
463 15
            $this->clearEntityChangeSet($oid);
464
465 15
            unset($this->scheduledForSynchronization[$this->em->getClassMetadata(\get_class($object))->rootEntityName][$oid]);
466
        }
467 15
    }
468
469
    /**
470
     * Computes the changesets of all entities scheduled for insertion.
471
     *
472
     * @return void
473
     */
474 1050
    private function computeScheduleInsertsChangeSets()
475
    {
476 1050
        foreach ($this->entityInsertions as $entity) {
477 1042
            $class = $this->em->getClassMetadata(get_class($entity));
478
479 1042
            $this->computeChangeSet($class, $entity);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
480
        }
481 1050
    }
482
483
    /**
484
     * Only flushes the given entity according to a ruleset that keeps the UoW consistent.
485
     *
486
     * 1. All entities scheduled for insertion, (orphan) removals and changes in collections are processed as well!
487
     * 2. Read Only entities are skipped.
488
     * 3. Proxies are skipped.
489
     * 4. Only if entity is properly managed.
490
     *
491
     * @param object $entity
492
     *
493
     * @return void
494
     *
495
     * @throws \InvalidArgumentException
496
     */
497 18
    private function computeSingleEntityChangeSet($entity)
498
    {
499 18
        $state = $this->getEntityState($entity);
500
501 18
        if ($state !== self::STATE_MANAGED && $state !== self::STATE_REMOVED) {
502 1
            throw new \InvalidArgumentException("Entity has to be managed or scheduled for removal for single computation " . self::objToStr($entity));
503
        }
504
505 17
        $class = $this->em->getClassMetadata(get_class($entity));
506
507 17
        if ($state === self::STATE_MANAGED && $class->isChangeTrackingDeferredImplicit()) {
508 16
            $this->persist($entity);
509
        }
510
511
        // Compute changes for INSERTed entities first. This must always happen even in this case.
512 17
        $this->computeScheduleInsertsChangeSets();
513
514 17
        if ($class->isReadOnly) {
0 ignored issues
show
Bug introduced by
Accessing isReadOnly on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
515
            return;
516
        }
517
518
        // Ignore uninitialized proxy objects
519 17
        if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
0 ignored issues
show
Bug introduced by
Accessing __isInitialized__ on the interface Doctrine\ORM\Proxy\Proxy suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
520 2
            return;
521
        }
522
523
        // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here.
524 15
        $oid = spl_object_hash($entity);
525
526 15 View Code Duplication
        if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
527 6
            $this->computeChangeSet($class, $entity);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
528
        }
529 15
    }
530
531
    /**
532
     * Executes any extra updates that have been scheduled.
533
     */
534 44
    private function executeExtraUpdates()
535
    {
536 44
        foreach ($this->extraUpdates as $oid => $update) {
537 44
            list ($entity, $changeset) = $update;
538
539 44
            $this->entityChangeSets[$oid] = $changeset;
540 44
            $this->getEntityPersister(get_class($entity))->update($entity);
541
        }
542
543 44
        $this->extraUpdates = [];
544 44
    }
545
546
    /**
547
     * Gets the changeset for an entity.
548
     *
549
     * @param object $entity
550
     *
551
     * @return array
552
     */
553 1039
    public function & getEntityChangeSet($entity)
554
    {
555 1039
        $oid  = spl_object_hash($entity);
556 1039
        $data = [];
557
558 1039
        if (!isset($this->entityChangeSets[$oid])) {
559 3
            return $data;
560
        }
561
562 1039
        return $this->entityChangeSets[$oid];
563
    }
564
565
    /**
566
     * Computes the changes that happened to a single entity.
567
     *
568
     * Modifies/populates the following properties:
569
     *
570
     * {@link _originalEntityData}
571
     * If the entity is NEW or MANAGED but not yet fully persisted (only has an id)
572
     * then it was not fetched from the database and therefore we have no original
573
     * entity data yet. All of the current entity data is stored as the original entity data.
574
     *
575
     * {@link _entityChangeSets}
576
     * The changes detected on all properties of the entity are stored there.
577
     * A change is a tuple array where the first entry is the old value and the second
578
     * entry is the new value of the property. Changesets are used by persisters
579
     * to INSERT/UPDATE the persistent entity state.
580
     *
581
     * {@link _entityUpdates}
582
     * If the entity is already fully MANAGED (has been fetched from the database before)
583
     * and any changes to its properties are detected, then a reference to the entity is stored
584
     * there to mark it for an update.
585
     *
586
     * {@link _collectionDeletions}
587
     * If a PersistentCollection has been de-referenced in a fully MANAGED entity,
588
     * then this collection is marked for deletion.
589
     *
590
     * @ignore
591
     *
592
     * @internal Don't call from the outside.
593
     *
594
     * @param ClassMetadata $class  The class descriptor of the entity.
595
     * @param object        $entity The entity for which to compute the changes.
596
     *
597
     * @return void
598
     */
599 1052
    public function computeChangeSet(ClassMetadata $class, $entity)
600
    {
601 1052
        $oid = spl_object_hash($entity);
602
603 1052
        if (isset($this->readOnlyObjects[$oid])) {
604 2
            return;
605
        }
606
607 1052
        if ( ! $class->isInheritanceTypeNone()) {
608 323
            $class = $this->em->getClassMetadata(get_class($entity));
609
        }
610
611 1052
        $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preFlush) & ~ListenersInvoker::INVOKE_MANAGER;
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
612
613 1052 View Code Duplication
        if ($invoke !== ListenersInvoker::INVOKE_NONE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
614 138
            $this->listenersInvoker->invoke($class, Events::preFlush, $entity, new PreFlushEventArgs($this->em), $invoke);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
615
        }
616
617 1052
        $actualData = [];
618
619 1052
        foreach ($class->reflFields as $name => $refProp) {
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
620 1052
            $value = $refProp->getValue($entity);
621
622 1052
            if ($class->isCollectionValuedAssociation($name) && $value !== null) {
623 792
                if ($value instanceof PersistentCollection) {
624 202
                    if ($value->getOwner() === $entity) {
625 202
                        continue;
626
                    }
627
628 5
                    $value = new ArrayCollection($value->getValues());
629
                }
630
631
                // If $value is not a Collection then use an ArrayCollection.
632 787
                if ( ! $value instanceof Collection) {
633 242
                    $value = new ArrayCollection($value);
634
                }
635
636 787
                $assoc = $class->associationMappings[$name];
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
637
638
                // Inject PersistentCollection
639 787
                $value = new PersistentCollection(
640 787
                    $this->em, $this->em->getClassMetadata($assoc['targetEntity']), $value
0 ignored issues
show
Compatibility introduced by
$this->em->getClassMetad...$assoc['targetEntity']) of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
641
                );
642 787
                $value->setOwner($entity, $assoc);
643 787
                $value->setDirty( ! $value->isEmpty());
644
645 787
                $class->reflFields[$name]->setValue($entity, $value);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
646
647 787
                $actualData[$name] = $value;
648
649 787
                continue;
650
            }
651
652 1052
            if (( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity()) && ($name !== $class->versionField)) {
0 ignored issues
show
Bug introduced by
Accessing versionField on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
653 1052
                $actualData[$name] = $value;
654
            }
655
        }
656
657 1052
        if ( ! isset($this->originalEntityData[$oid])) {
658
            // Entity is either NEW or MANAGED but not yet fully persisted (only has an id).
659
            // These result in an INSERT.
660 1048
            $this->originalEntityData[$oid] = $actualData;
661 1048
            $changeSet = [];
662
663 1048
            foreach ($actualData as $propName => $actualValue) {
664 1026 View Code Duplication
                if ( ! isset($class->associationMappings[$propName])) {
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
665 974
                    $changeSet[$propName] = [null, $actualValue];
666
667 974
                    continue;
668
                }
669
670 916
                $assoc = $class->associationMappings[$propName];
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
671
672 916
                if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
673 916
                    $changeSet[$propName] = [null, $actualValue];
674
                }
675
            }
676
677 1048
            $this->entityChangeSets[$oid] = $changeSet;
678
        } else {
679
            // Entity is "fully" MANAGED: it was already fully persisted before
680
            // and we have a copy of the original data
681 270
            $originalData           = $this->originalEntityData[$oid];
682 270
            $isChangeTrackingNotify = $class->isChangeTrackingNotify();
683 270
            $changeSet              = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid]))
684
                ? $this->entityChangeSets[$oid]
685 270
                : [];
686
687 270
            foreach ($actualData as $propName => $actualValue) {
688
                // skip field, its a partially omitted one!
689 255
                if ( ! (isset($originalData[$propName]) || array_key_exists($propName, $originalData))) {
690 8
                    continue;
691
                }
692
693 255
                $orgValue = $originalData[$propName];
694
695
                // skip if value haven't changed
696 255
                if ($orgValue === $actualValue) {
697 239
                    continue;
698
                }
699
700
                // if regular field
701 115 View Code Duplication
                if ( ! isset($class->associationMappings[$propName])) {
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
702 60
                    if ($isChangeTrackingNotify) {
703
                        continue;
704
                    }
705
706 60
                    $changeSet[$propName] = [$orgValue, $actualValue];
707
708 60
                    continue;
709
                }
710
711 59
                $assoc = $class->associationMappings[$propName];
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
712
713
                // Persistent collection was exchanged with the "originally"
714
                // created one. This can only mean it was cloned and replaced
715
                // on another entity.
716 59
                if ($actualValue instanceof PersistentCollection) {
717 8
                    $owner = $actualValue->getOwner();
718 8
                    if ($owner === null) { // cloned
719
                        $actualValue->setOwner($entity, $assoc);
720 8
                    } else if ($owner !== $entity) { // no clone, we have to fix
721
                        if (!$actualValue->isInitialized()) {
722
                            $actualValue->initialize(); // we have to do this otherwise the cols share state
723
                        }
724
                        $newValue = clone $actualValue;
725
                        $newValue->setOwner($entity, $assoc);
726
                        $class->reflFields[$propName]->setValue($entity, $newValue);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
727
                    }
728
                }
729
730 59
                if ($orgValue instanceof PersistentCollection) {
731
                    // A PersistentCollection was de-referenced, so delete it.
732 8
                    $coid = spl_object_hash($orgValue);
733
734 8
                    if (isset($this->collectionDeletions[$coid])) {
735
                        continue;
736
                    }
737
738 8
                    $this->collectionDeletions[$coid] = $orgValue;
739 8
                    $changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored.
740
741 8
                    continue;
742
                }
743
744 51
                if ($assoc['type'] & ClassMetadata::TO_ONE) {
745 50
                    if ($assoc['isOwningSide']) {
746 22
                        $changeSet[$propName] = [$orgValue, $actualValue];
747
                    }
748
749 50
                    if ($orgValue !== null && $assoc['orphanRemoval']) {
750 51
                        $this->scheduleOrphanRemoval($orgValue);
751
                    }
752
                }
753
            }
754
755 270
            if ($changeSet) {
756 88
                $this->entityChangeSets[$oid]   = $changeSet;
757 88
                $this->originalEntityData[$oid] = $actualData;
758 88
                $this->entityUpdates[$oid]      = $entity;
759
            }
760
        }
761
762
        // Look for changes in associations of the entity
763 1052
        foreach ($class->associationMappings as $field => $assoc) {
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
764 916
            if (($val = $class->reflFields[$field]->getValue($entity)) === null) {
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
765 644
                continue;
766
            }
767
768 887
            $this->computeAssociationChanges($assoc, $val);
769
770 881
            if ( ! isset($this->entityChangeSets[$oid]) &&
771 881
                $assoc['isOwningSide'] &&
772 881
                $assoc['type'] == ClassMetadata::MANY_TO_MANY &&
773 881
                $val instanceof PersistentCollection &&
774 881
                $val->isDirty()) {
775
776 35
                $this->entityChangeSets[$oid]   = [];
777 35
                $this->originalEntityData[$oid] = $actualData;
778 881
                $this->entityUpdates[$oid]      = $entity;
779
            }
780
        }
781 1046
    }
782
783
    /**
784
     * Computes all the changes that have been done to entities and collections
785
     * since the last commit and stores these changes in the _entityChangeSet map
786
     * temporarily for access by the persisters, until the UoW commit is finished.
787
     *
788
     * @return void
789
     */
790 1041
    public function computeChangeSets()
791
    {
792
        // Compute changes for INSERTed entities first. This must always happen.
793 1041
        $this->computeScheduleInsertsChangeSets();
794
795
        // Compute changes for other MANAGED entities. Change tracking policies take effect here.
796 1041
        foreach ($this->identityMap as $className => $entities) {
797 462
            $class = $this->em->getClassMetadata($className);
798
799
            // Skip class if instances are read-only
800 462
            if ($class->isReadOnly) {
0 ignored issues
show
Bug introduced by
Accessing isReadOnly on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
801 1
                continue;
802
            }
803
804
            // If change tracking is explicit or happens through notification, then only compute
805
            // changes on entities of that type that are explicitly marked for synchronization.
806
            switch (true) {
807 461
                case ($class->isChangeTrackingDeferredImplicit()):
808 459
                    $entitiesToProcess = $entities;
809 459
                    break;
810
811 3
                case (isset($this->scheduledForSynchronization[$className])):
812 3
                    $entitiesToProcess = $this->scheduledForSynchronization[$className];
813 3
                    break;
814
815
                default:
816 1
                    $entitiesToProcess = [];
817
818
            }
819
820 461
            foreach ($entitiesToProcess as $entity) {
821
                // Ignore uninitialized proxy objects
822 441
                if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
0 ignored issues
show
Bug introduced by
Accessing __isInitialized__ on the interface Doctrine\ORM\Proxy\Proxy suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
823 36
                    continue;
824
                }
825
826
                // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here.
827 440
                $oid = spl_object_hash($entity);
828
829 440 View Code Duplication
                if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
830 461
                    $this->computeChangeSet($class, $entity);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
831
                }
832
            }
833
        }
834 1041
    }
835
836
    /**
837
     * Computes the changes of an association.
838
     *
839
     * @param array $assoc The association mapping.
840
     * @param mixed $value The value of the association.
841
     *
842
     * @throws ORMInvalidArgumentException
843
     * @throws ORMException
844
     *
845
     * @return void
846
     */
847 887
    private function computeAssociationChanges($assoc, $value)
848
    {
849 887
        if ($value instanceof Proxy && ! $value->__isInitialized__) {
0 ignored issues
show
Bug introduced by
Accessing __isInitialized__ on the interface Doctrine\ORM\Proxy\Proxy suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
850 29
            return;
851
        }
852
853 886
        if ($value instanceof PersistentCollection && $value->isDirty()) {
854 537
            $coid = spl_object_hash($value);
855
856 537
            $this->collectionUpdates[$coid] = $value;
857 537
            $this->visitedCollections[$coid] = $value;
858
        }
859
860
        // Look through the entities, and in any of their associations,
861
        // for transient (new) entities, recursively. ("Persistence by reachability")
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
868 6
                throw ORMInvalidArgumentException::invalidAssociation($targetClass, $assoc, $entry);
0 ignored issues
show
Compatibility introduced by
$targetClass of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
869
            }
870
871 732
            $state = $this->getEntityState($entry, self::STATE_NEW);
872
873 732
            if ( ! ($entry instanceof $assoc['targetEntity'])) {
874
                throw ORMException::unexpectedAssociationValue($assoc['sourceEntity'], $assoc['fieldName'], get_class($entry), $assoc['targetEntity']);
875
            }
876
877
            switch ($state) {
878 732
                case self::STATE_NEW:
879 42
                    if ( ! $assoc['isCascadePersist']) {
880
                        /*
881
                         * For now just record the details, because this may
882
                         * not be an issue if we later discover another pathway
883
                         * through the object-graph where cascade-persistence
884
                         * is enabled for this object.
885
                         */
886 6
                        $this->nonCascadedNewDetectedEntities[\spl_object_hash($entry)] = [$assoc, $entry];
887
888 6
                        break;
889
                    }
890
891 37
                    $this->persistNew($targetClass, $entry);
0 ignored issues
show
Compatibility introduced by
$targetClass of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
892 37
                    $this->computeChangeSet($targetClass, $entry);
0 ignored issues
show
Compatibility introduced by
$targetClass of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
893
894 37
                    break;
895
896 724
                case self::STATE_REMOVED:
897
                    // Consume the $value as array (it's either an array or an ArrayAccess)
898
                    // and remove the element from Collection.
899 4
                    if ($assoc['type'] & ClassMetadata::TO_MANY) {
900 3
                        unset($value[$key]);
901
                    }
902 4
                    break;
903
904 724
                case self::STATE_DETACHED:
905
                    // Can actually not happen right now as we assume STATE_NEW,
906
                    // so the exception will be raised from the DBAL layer (constraint violation).
907
                    throw ORMInvalidArgumentException::detachedEntityFoundThroughRelationship($assoc, $entry);
908
                    break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

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

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

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

    return false;
}

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

Loading history...
909
910 732
                default:
911
                    // MANAGED associated entities are already taken into account
912
                    // during changeset calculation anyway, since they are in the identity map.
913
            }
914
        }
915 880
    }
916
917
    /**
918
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
919
     * @param object                              $entity
920
     *
921
     * @return void
922
     */
923 1071
    private function persistNew($class, $entity)
924
    {
925 1071
        $oid    = spl_object_hash($entity);
926 1071
        $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::prePersist);
927
928 1071 View Code Duplication
        if ($invoke !== ListenersInvoker::INVOKE_NONE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
929 141
            $this->listenersInvoker->invoke($class, Events::prePersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
930
        }
931
932 1071
        $idGen = $class->idGenerator;
933
934 1071
        if ( ! $idGen->isPostInsertGenerator()) {
935 282
            $idValue = $idGen->generate($this->em, $entity);
0 ignored issues
show
Compatibility introduced by
$this->em of type object<Doctrine\ORM\EntityManagerInterface> is not a sub-type of object<Doctrine\ORM\EntityManager>. It seems like you assume a concrete implementation of the interface Doctrine\ORM\EntityManagerInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
936
937 282
            if ( ! $idGen instanceof \Doctrine\ORM\Id\AssignedGenerator) {
938 2
                $idValue = [$class->getSingleIdentifierFieldName() => $this->convertSingleFieldIdentifierToPHPValue($class, $idValue)];
939
940 2
                $class->setIdentifierValues($entity, $idValue);
941
            }
942
943 282
            $this->entityIdentifiers[$oid] = $idValue;
944
        }
945
946 1071
        $this->entityStates[$oid] = self::STATE_MANAGED;
947
948 1071
        $this->scheduleForInsert($entity);
949 1071
    }
950
951
    /**
952
     * INTERNAL:
953
     * Computes the changeset of an individual entity, independently of the
954
     * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit().
955
     *
956
     * The passed entity must be a managed entity. If the entity already has a change set
957
     * because this method is invoked during a commit cycle then the change sets are added.
958
     * whereby changes detected in this method prevail.
959
     *
960
     * @ignore
961
     *
962
     * @param ClassMetadata $class  The class descriptor of the entity.
963
     * @param object        $entity The entity for which to (re)calculate the change set.
964
     *
965
     * @return void
966
     *
967
     * @throws ORMInvalidArgumentException If the passed entity is not MANAGED.
968
     */
969 16
    public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity)
970
    {
971 16
        $oid = spl_object_hash($entity);
972
973 16
        if ( ! isset($this->entityStates[$oid]) || $this->entityStates[$oid] != self::STATE_MANAGED) {
974
            throw ORMInvalidArgumentException::entityNotManaged($entity);
975
        }
976
977
        // skip if change tracking is "NOTIFY"
978 16
        if ($class->isChangeTrackingNotify()) {
979
            return;
980
        }
981
982 16
        if ( ! $class->isInheritanceTypeNone()) {
983 3
            $class = $this->em->getClassMetadata(get_class($entity));
984
        }
985
986 16
        $actualData = [];
987
988 16
        foreach ($class->reflFields as $name => $refProp) {
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
989 16
            if (( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity())
990 16
                && ($name !== $class->versionField)
0 ignored issues
show
Bug introduced by
Accessing versionField on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
991 16
                && ! $class->isCollectionValuedAssociation($name)) {
992 16
                $actualData[$name] = $refProp->getValue($entity);
993
            }
994
        }
995
996 16
        if ( ! isset($this->originalEntityData[$oid])) {
997
            throw new \RuntimeException('Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.');
998
        }
999
1000 16
        $originalData = $this->originalEntityData[$oid];
1001 16
        $changeSet = [];
1002
1003 16
        foreach ($actualData as $propName => $actualValue) {
1004 16
            $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
1005
1006 16
            if ($orgValue !== $actualValue) {
1007 16
                $changeSet[$propName] = [$orgValue, $actualValue];
1008
            }
1009
        }
1010
1011 16
        if ($changeSet) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $changeSet of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

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

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

Available Fixes

  1. Adding an additional type check:

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

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

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

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1093 77
                continue;
1094
            }
1095
1096 119
            if ($preUpdateInvoke != ListenersInvoker::INVOKE_NONE) {
1097 13
                $this->listenersInvoker->invoke($class, Events::preUpdate, $entity, new PreUpdateEventArgs($entity, $this->em, $this->getEntityChangeSet($entity)), $preUpdateInvoke);
1098
1099 13
                $this->recomputeSingleEntityChangeSet($class, $entity);
1100
            }
1101
1102 119
            if ( ! empty($this->entityChangeSets[$oid])) {
1103 85
                $persister->update($entity);
1104
            }
1105
1106 115
            unset($this->entityUpdates[$oid]);
1107
1108 115 View Code Duplication
            if ($postUpdateInvoke != ListenersInvoker::INVOKE_NONE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1109 115
                $this->listenersInvoker->invoke($class, Events::postUpdate, $entity, new LifecycleEventArgs($entity, $this->em), $postUpdateInvoke);
1110
            }
1111
        }
1112 115
    }
1113
1114
    /**
1115
     * Executes all entity deletions for entities of the specified type.
1116
     *
1117
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
1118
     *
1119
     * @return void
1120
     */
1121 63
    private function executeDeletions($class)
1122
    {
1123 63
        $className  = $class->name;
1124 63
        $persister  = $this->getEntityPersister($className);
1125 63
        $invoke     = $this->listenersInvoker->getSubscribedSystems($class, Events::postRemove);
1126
1127 63
        foreach ($this->entityDeletions as $oid => $entity) {
1128 63
            if ($this->em->getClassMetadata(get_class($entity))->name !== $className) {
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

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

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

Loading history...
1144 63
            if ( ! $class->isIdentifierNatural()) {
1145 53
                $class->reflFields[$class->identifier[0]]->setValue($entity, null);
1146
            }
1147
1148 63 View Code Duplication
            if ($invoke !== ListenersInvoker::INVOKE_NONE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1206
1207
                // If the target class has mapped subclasses, these share the same dependency.
1208 858
                if ( ! $targetClass->subClasses) {
0 ignored issues
show
Bug introduced by
Accessing subClasses on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1209 851
                    continue;
1210
                }
1211
1212 226
                foreach ($targetClass->subClasses as $subClassName) {
0 ignored issues
show
Bug introduced by
Accessing subClasses on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1213 226
                    $targetSubClass = $this->em->getClassMetadata($subClassName);
1214
1215 226
                    if ( ! $calc->hasNode($subClassName)) {
1216 196
                        $calc->addNode($targetSubClass->name, $targetSubClass);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1222
                }
1223
            }
1224
        }
1225
1226 1044
        return $calc->sort();
1227
    }
1228
1229
    /**
1230
     * Schedules an entity for insertion into the database.
1231
     * If the entity already has an identifier, it will be added to the identity map.
1232
     *
1233
     * @param object $entity The entity to schedule for insertion.
1234
     *
1235
     * @return void
1236
     *
1237
     * @throws ORMInvalidArgumentException
1238
     * @throws \InvalidArgumentException
1239
     */
1240 1072
    public function scheduleForInsert($entity)
1241
    {
1242 1072
        $oid = spl_object_hash($entity);
1243
1244 1072
        if (isset($this->entityUpdates[$oid])) {
1245
            throw new InvalidArgumentException("Dirty entity can not be scheduled for insertion.");
1246
        }
1247
1248 1072
        if (isset($this->entityDeletions[$oid])) {
1249 1
            throw ORMInvalidArgumentException::scheduleInsertForRemovedEntity($entity);
1250
        }
1251 1072
        if (isset($this->originalEntityData[$oid]) && ! isset($this->entityInsertions[$oid])) {
1252 1
            throw ORMInvalidArgumentException::scheduleInsertForManagedEntity($entity);
1253
        }
1254
1255 1072
        if (isset($this->entityInsertions[$oid])) {
1256 1
            throw ORMInvalidArgumentException::scheduleInsertTwice($entity);
1257
        }
1258
1259 1072
        $this->entityInsertions[$oid] = $entity;
1260
1261 1072
        if (isset($this->entityIdentifiers[$oid])) {
1262 282
            $this->addToIdentityMap($entity);
1263
        }
1264
1265 1072
        if ($entity instanceof NotifyPropertyChanged) {
1266 7
            $entity->addPropertyChangedListener($this);
1267
        }
1268 1072
    }
1269
1270
    /**
1271
     * Checks whether an entity is scheduled for insertion.
1272
     *
1273
     * @param object $entity
1274
     *
1275
     * @return boolean
1276
     */
1277 646
    public function isScheduledForInsert($entity)
1278
    {
1279 646
        return isset($this->entityInsertions[spl_object_hash($entity)]);
1280
    }
1281
1282
    /**
1283
     * Schedules an entity for being updated.
1284
     *
1285
     * @param object $entity The entity to schedule for being updated.
1286
     *
1287
     * @return void
1288
     *
1289
     * @throws ORMInvalidArgumentException
1290
     */
1291 1
    public function scheduleForUpdate($entity)
1292
    {
1293 1
        $oid = spl_object_hash($entity);
1294
1295 1
        if ( ! isset($this->entityIdentifiers[$oid])) {
1296
            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "scheduling for update");
1297
        }
1298
1299 1
        if (isset($this->entityDeletions[$oid])) {
1300
            throw ORMInvalidArgumentException::entityIsRemoved($entity, "schedule for update");
1301
        }
1302
1303 1
        if ( ! isset($this->entityUpdates[$oid]) && ! isset($this->entityInsertions[$oid])) {
1304 1
            $this->entityUpdates[$oid] = $entity;
1305
        }
1306 1
    }
1307
1308
    /**
1309
     * INTERNAL:
1310
     * Schedules an extra update that will be executed immediately after the
1311
     * regular entity updates within the currently running commit cycle.
1312
     *
1313
     * Extra updates for entities are stored as (entity, changeset) tuples.
1314
     *
1315
     * @ignore
1316
     *
1317
     * @param object $entity    The entity for which to schedule an extra update.
1318
     * @param array  $changeset The changeset of the entity (what to update).
1319
     *
1320
     * @return void
1321
     */
1322 44
    public function scheduleExtraUpdate($entity, array $changeset)
1323
    {
1324 44
        $oid         = spl_object_hash($entity);
1325 44
        $extraUpdate = [$entity, $changeset];
1326
1327 44
        if (isset($this->extraUpdates[$oid])) {
1328 1
            list(, $changeset2) = $this->extraUpdates[$oid];
1329
1330 1
            $extraUpdate = [$entity, $changeset + $changeset2];
1331
        }
1332
1333 44
        $this->extraUpdates[$oid] = $extraUpdate;
1334 44
    }
1335
1336
    /**
1337
     * Checks whether an entity is registered as dirty in the unit of work.
1338
     * Note: Is not very useful currently as dirty entities are only registered
1339
     * at commit time.
1340
     *
1341
     * @param object $entity
1342
     *
1343
     * @return boolean
1344
     */
1345
    public function isScheduledForUpdate($entity)
1346
    {
1347
        return isset($this->entityUpdates[spl_object_hash($entity)]);
1348
    }
1349
1350
    /**
1351
     * Checks whether an entity is registered to be checked in the unit of work.
1352
     *
1353
     * @param object $entity
1354
     *
1355
     * @return boolean
1356
     */
1357 1
    public function isScheduledForDirtyCheck($entity)
1358
    {
1359 1
        $rootEntityName = $this->em->getClassMetadata(get_class($entity))->rootEntityName;
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1360
1361 1
        return isset($this->scheduledForSynchronization[$rootEntityName][spl_object_hash($entity)]);
1362
    }
1363
1364
    /**
1365
     * INTERNAL:
1366
     * Schedules an entity for deletion.
1367
     *
1368
     * @param object $entity
1369
     *
1370
     * @return void
1371
     */
1372 66
    public function scheduleForDelete($entity)
1373
    {
1374 66
        $oid = spl_object_hash($entity);
1375
1376 66
        if (isset($this->entityInsertions[$oid])) {
1377 1
            if ($this->isInIdentityMap($entity)) {
1378
                $this->removeFromIdentityMap($entity);
1379
            }
1380
1381 1
            unset($this->entityInsertions[$oid], $this->entityStates[$oid]);
1382
1383 1
            return; // entity has not been persisted yet, so nothing more to do.
1384
        }
1385
1386 66
        if ( ! $this->isInIdentityMap($entity)) {
1387 1
            return;
1388
        }
1389
1390 65
        $this->removeFromIdentityMap($entity);
1391
1392 65
        unset($this->entityUpdates[$oid]);
1393
1394 65
        if ( ! isset($this->entityDeletions[$oid])) {
1395 65
            $this->entityDeletions[$oid] = $entity;
1396 65
            $this->entityStates[$oid]    = self::STATE_REMOVED;
1397
        }
1398 65
    }
1399
1400
    /**
1401
     * Checks whether an entity is registered as removed/deleted with the unit
1402
     * of work.
1403
     *
1404
     * @param object $entity
1405
     *
1406
     * @return boolean
1407
     */
1408 17
    public function isScheduledForDelete($entity)
1409
    {
1410 17
        return isset($this->entityDeletions[spl_object_hash($entity)]);
1411
    }
1412
1413
    /**
1414
     * Checks whether an entity is scheduled for insertion, update or deletion.
1415
     *
1416
     * @param object $entity
1417
     *
1418
     * @return boolean
1419
     */
1420
    public function isEntityScheduled($entity)
1421
    {
1422
        $oid = spl_object_hash($entity);
1423
1424
        return isset($this->entityInsertions[$oid])
1425
            || isset($this->entityUpdates[$oid])
1426
            || isset($this->entityDeletions[$oid]);
1427
    }
1428
1429
    /**
1430
     * INTERNAL:
1431
     * Registers an entity in the identity map.
1432
     * Note that entities in a hierarchy are registered with the class name of
1433
     * the root entity.
1434
     *
1435
     * @ignore
1436
     *
1437
     * @param object $entity The entity to register.
1438
     *
1439
     * @return boolean TRUE if the registration was successful, FALSE if the identity of
1440
     *                 the entity in question is already managed.
1441
     *
1442
     * @throws ORMInvalidArgumentException
1443
     */
1444 1138
    public function addToIdentityMap($entity)
1445
    {
1446 1138
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1447 1138
        $identifier    = $this->entityIdentifiers[spl_object_hash($entity)];
1448
1449 1138
        if (empty($identifier) || in_array(null, $identifier, true)) {
1450 6
            throw ORMInvalidArgumentException::entityWithoutIdentity($classMetadata->name, $entity);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1451
        }
1452
1453 1132
        $idHash    = implode(' ', $identifier);
1454 1132
        $className = $classMetadata->rootEntityName;
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

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

Loading history...
1496 5
            return self::STATE_NEW;
1497
        }
1498
1499 10
        if ($class->containsForeignIdentifier) {
0 ignored issues
show
Bug introduced by
Accessing containsForeignIdentifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1500 1
            $id = $this->identifierFlattener->flattenIdentifier($class, $id);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1501
        }
1502
1503
        switch (true) {
1504 10
            case ($class->isIdentifierNatural()):
0 ignored issues
show
Bug introduced by
The method isIdentifierNatural() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. Did you maybe mean isIdentifier()?

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

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

Loading history...
1505
                // Check for a version field, if available, to avoid a db lookup.
1506 5
                if ($class->isVersioned) {
0 ignored issues
show
Bug introduced by
Accessing isVersioned on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1508
                        ? self::STATE_DETACHED
1509 1
                        : self::STATE_NEW;
1510
                }
1511
1512
                // Last try before db lookup: check the identity map.
1513 4
                if ($this->tryGetById($id, $class->rootEntityName)) {
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1514 1
                    return self::STATE_DETACHED;
1515
                }
1516
1517
                // db lookup
1518 4
                if ($this->getEntityPersister($class->name)->exists($entity)) {
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1519
                    return self::STATE_DETACHED;
1520
                }
1521
1522 4
                return self::STATE_NEW;
1523
1524 5
            case ( ! $class->idGenerator->isPostInsertGenerator()):
0 ignored issues
show
Bug introduced by
Accessing idGenerator on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1525
                // if we have a pre insert generator we can't be sure that having an id
1526
                // really means that the entity exists. We have to verify this through
1527
                // the last resort: a db lookup
1528
1529
                // Last try before db lookup: check the identity map.
1530
                if ($this->tryGetById($id, $class->rootEntityName)) {
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1531
                    return self::STATE_DETACHED;
1532
                }
1533
1534
                // db lookup
1535
                if ($this->getEntityPersister($class->name)->exists($entity)) {
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1536
                    return self::STATE_DETACHED;
1537
                }
1538
1539
                return self::STATE_NEW;
1540
1541
            default:
1542 5
                return self::STATE_DETACHED;
1543
        }
1544
    }
1545
1546
    /**
1547
     * INTERNAL:
1548
     * Removes an entity from the identity map. This effectively detaches the
1549
     * entity from the persistence management of Doctrine.
1550
     *
1551
     * @ignore
1552
     *
1553
     * @param object $entity
1554
     *
1555
     * @return boolean
1556
     *
1557
     * @throws ORMInvalidArgumentException
1558
     */
1559 78
    public function removeFromIdentityMap($entity)
1560
    {
1561 78
        $oid           = spl_object_hash($entity);
1562 78
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1563 78
        $idHash        = implode(' ', $this->entityIdentifiers[$oid]);
1564
1565 78
        if ($idHash === '') {
1566
            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "remove from identity map");
1567
        }
1568
1569 78
        $className = $classMetadata->rootEntityName;
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1570
1571 78
        if (isset($this->identityMap[$className][$idHash])) {
1572 78
            unset($this->identityMap[$className][$idHash]);
1573 78
            unset($this->readOnlyObjects[$oid]);
1574
1575
            //$this->entityStates[$oid] = self::STATE_DETACHED;
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1576
1577 78
            return true;
1578
        }
1579
1580
        return false;
1581
    }
1582
1583
    /**
1584
     * INTERNAL:
1585
     * Gets an entity in the identity map by its identifier hash.
1586
     *
1587
     * @ignore
1588
     *
1589
     * @param string $idHash
1590
     * @param string $rootClassName
1591
     *
1592
     * @return object
1593
     */
1594 6
    public function getByIdHash($idHash, $rootClassName)
1595
    {
1596 6
        return $this->identityMap[$rootClassName][$idHash];
1597
    }
1598
1599
    /**
1600
     * INTERNAL:
1601
     * Tries to get an entity by its identifier hash. If no entity is found for
1602
     * the given hash, FALSE is returned.
1603
     *
1604
     * @ignore
1605
     *
1606
     * @param mixed  $idHash        (must be possible to cast it to string)
1607
     * @param string $rootClassName
1608
     *
1609
     * @return object|bool The found entity or FALSE.
1610
     */
1611 35 View Code Duplication
    public function tryGetByIdHash($idHash, $rootClassName)
1612
    {
1613 35
        $stringIdHash = (string) $idHash;
1614
1615 35
        return isset($this->identityMap[$rootClassName][$stringIdHash])
1616 35
            ? $this->identityMap[$rootClassName][$stringIdHash]
1617 35
            : false;
1618
    }
1619
1620
    /**
1621
     * Checks whether an entity is registered in the identity map of this UnitOfWork.
1622
     *
1623
     * @param object $entity
1624
     *
1625
     * @return boolean
1626
     */
1627 217
    public function isInIdentityMap($entity)
1628
    {
1629 217
        $oid = spl_object_hash($entity);
1630
1631 217
        if (empty($this->entityIdentifiers[$oid])) {
1632 33
            return false;
1633
        }
1634
1635 201
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1636 201
        $idHash        = implode(' ', $this->entityIdentifiers[$oid]);
1637
1638 201
        return isset($this->identityMap[$classMetadata->rootEntityName][$idHash]);
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1639
    }
1640
1641
    /**
1642
     * INTERNAL:
1643
     * Checks whether an identifier hash exists in the identity map.
1644
     *
1645
     * @ignore
1646
     *
1647
     * @param string $idHash
1648
     * @param string $rootClassName
1649
     *
1650
     * @return boolean
1651
     */
1652
    public function containsIdHash($idHash, $rootClassName)
1653
    {
1654
        return isset($this->identityMap[$rootClassName][$idHash]);
1655
    }
1656
1657
    /**
1658
     * Persists an entity as part of the current unit of work.
1659
     *
1660
     * @param object $entity The entity to persist.
1661
     *
1662
     * @return void
1663
     */
1664 1067
    public function persist($entity)
1665
    {
1666 1067
        $visited = [];
1667
1668 1067
        $this->doPersist($entity, $visited);
1669 1060
    }
1670
1671
    /**
1672
     * Persists an entity as part of the current unit of work.
1673
     *
1674
     * This method is internally called during persist() cascades as it tracks
1675
     * the already visited entities to prevent infinite recursions.
1676
     *
1677
     * @param object $entity  The entity to persist.
1678
     * @param array  $visited The already visited entities.
1679
     *
1680
     * @return void
1681
     *
1682
     * @throws ORMInvalidArgumentException
1683
     * @throws UnexpectedValueException
1684
     */
1685 1067
    private function doPersist($entity, array &$visited)
1686
    {
1687 1067
        $oid = spl_object_hash($entity);
1688
1689 1067
        if (isset($visited[$oid])) {
1690 110
            return; // Prevent infinite recursion
1691
        }
1692
1693 1067
        $visited[$oid] = $entity; // Mark visited
1694
1695 1067
        $class = $this->em->getClassMetadata(get_class($entity));
1696
1697
        // We assume NEW, so DETACHED entities result in an exception on flush (constraint violation).
1698
        // If we would detect DETACHED here we would throw an exception anyway with the same
1699
        // consequences (not recoverable/programming error), so just assuming NEW here
1700
        // lets us avoid some database lookups for entities with natural identifiers.
1701 1067
        $entityState = $this->getEntityState($entity, self::STATE_NEW);
1702
1703
        switch ($entityState) {
1704 1067
            case self::STATE_MANAGED:
1705
                // Nothing to do, except if policy is "deferred explicit"
1706 238
                if ($class->isChangeTrackingDeferredExplicit()) {
1707 2
                    $this->scheduleForDirtyCheck($entity);
1708
                }
1709 238
                break;
1710
1711 1067
            case self::STATE_NEW:
1712 1066
                $this->persistNew($class, $entity);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1713 1066
                break;
1714
1715 1
            case self::STATE_REMOVED:
1716
                // Entity becomes managed again
1717 1
                unset($this->entityDeletions[$oid]);
1718 1
                $this->addToIdentityMap($entity);
1719
1720 1
                $this->entityStates[$oid] = self::STATE_MANAGED;
1721 1
                break;
1722
1723
            case self::STATE_DETACHED:
1724
                // Can actually not happen right now since we assume STATE_NEW.
1725
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "persisted");
1726
1727
            default:
1728
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1729
        }
1730
1731 1067
        $this->cascadePersist($entity, $visited);
1732 1060
    }
1733
1734
    /**
1735
     * Deletes an entity as part of the current unit of work.
1736
     *
1737
     * @param object $entity The entity to remove.
1738
     *
1739
     * @return void
1740
     */
1741 65
    public function remove($entity)
1742
    {
1743 65
        $visited = [];
1744
1745 65
        $this->doRemove($entity, $visited);
1746 65
    }
1747
1748
    /**
1749
     * Deletes an entity as part of the current unit of work.
1750
     *
1751
     * This method is internally called during delete() cascades as it tracks
1752
     * the already visited entities to prevent infinite recursions.
1753
     *
1754
     * @param object $entity  The entity to delete.
1755
     * @param array  $visited The map of the already visited entities.
1756
     *
1757
     * @return void
1758
     *
1759
     * @throws ORMInvalidArgumentException If the instance is a detached entity.
1760
     * @throws UnexpectedValueException
1761
     */
1762 65
    private function doRemove($entity, array &$visited)
1763
    {
1764 65
        $oid = spl_object_hash($entity);
1765
1766 65
        if (isset($visited[$oid])) {
1767 1
            return; // Prevent infinite recursion
1768
        }
1769
1770 65
        $visited[$oid] = $entity; // mark visited
1771
1772
        // Cascade first, because scheduleForDelete() removes the entity from the identity map, which
1773
        // can cause problems when a lazy proxy has to be initialized for the cascade operation.
1774 65
        $this->cascadeRemove($entity, $visited);
1775
1776 65
        $class       = $this->em->getClassMetadata(get_class($entity));
1777 65
        $entityState = $this->getEntityState($entity);
1778
1779
        switch ($entityState) {
1780 65
            case self::STATE_NEW:
1781 65
            case self::STATE_REMOVED:
1782
                // nothing to do
1783 2
                break;
1784
1785 65
            case self::STATE_MANAGED:
1786 65
                $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preRemove);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1787
1788 65 View Code Duplication
                if ($invoke !== ListenersInvoker::INVOKE_NONE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1789 8
                    $this->listenersInvoker->invoke($class, Events::preRemove, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1790
                }
1791
1792 65
                $this->scheduleForDelete($entity);
1793 65
                break;
1794
1795
            case self::STATE_DETACHED:
1796
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "removed");
1797
            default:
1798
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1799
        }
1800
1801 65
    }
1802
1803
    /**
1804
     * Merges the state of the given detached entity into this UnitOfWork.
1805
     *
1806
     * @param object $entity
1807
     *
1808
     * @return object The managed copy of the entity.
1809
     *
1810
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1811
     *         attribute and the version check against the managed copy fails.
1812
     *
1813
     * @todo Require active transaction!? OptimisticLockException may result in undefined state!?
1814
     */
1815 43
    public function merge($entity)
1816
    {
1817 43
        $visited = [];
1818
1819 43
        return $this->doMerge($entity, $visited);
1820
    }
1821
1822
    /**
1823
     * Executes a merge operation on an entity.
1824
     *
1825
     * @param object      $entity
1826
     * @param array       $visited
1827
     * @param object|null $prevManagedCopy
1828
     * @param array|null  $assoc
1829
     *
1830
     * @return object The managed copy of the entity.
1831
     *
1832
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1833
     *         attribute and the version check against the managed copy fails.
1834
     * @throws ORMInvalidArgumentException If the entity instance is NEW.
1835
     * @throws EntityNotFoundException if an assigned identifier is used in the entity, but none is provided
1836
     */
1837 43
    private function doMerge($entity, array &$visited, $prevManagedCopy = null, array $assoc = [])
1838
    {
1839 43
        $oid = spl_object_hash($entity);
1840
1841 43
        if (isset($visited[$oid])) {
1842 4
            $managedCopy = $visited[$oid];
1843
1844 4
            if ($prevManagedCopy !== null) {
1845 4
                $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy);
1846
            }
1847
1848 4
            return $managedCopy;
1849
        }
1850
1851 43
        $class = $this->em->getClassMetadata(get_class($entity));
1852
1853
        // First we assume DETACHED, although it can still be NEW but we can avoid
1854
        // an extra db-roundtrip this way. If it is not MANAGED but has an identity,
1855
        // we need to fetch it from the db anyway in order to merge.
1856
        // MANAGED entities are ignored by the merge operation.
1857 43
        $managedCopy = $entity;
1858
1859 43
        if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) {
1860
            // Try to look the entity up in the identity map.
1861 42
            $id = $class->getIdentifierValues($entity);
1862
1863
            // If there is no ID, it is actually NEW.
1864 42
            if ( ! $id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
1865 6
                $managedCopy = $this->newInstance($class);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1866
1867 6
                $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy);
1868 6
                $this->persistNew($class, $managedCopy);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1869
            } else {
1870 37
                $flatId = ($class->containsForeignIdentifier)
0 ignored issues
show
Bug introduced by
Accessing containsForeignIdentifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1871 3
                    ? $this->identifierFlattener->flattenIdentifier($class, $id)
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1872 37
                    : $id;
1873
1874 37
                $managedCopy = $this->tryGetById($flatId, $class->rootEntityName);
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Bug Compatibility introduced by
The expression $this->tryGetById($flatI...class->rootEntityName); of type object|boolean adds the type boolean to the return on line 1923 which is incompatible with the return type documented by Doctrine\ORM\UnitOfWork::doMerge of type object.
Loading history...
1875
1876 37
                if ($managedCopy) {
1877
                    // We have the entity in-memory already, just make sure its not removed.
1878 15
                    if ($this->getEntityState($managedCopy) == self::STATE_REMOVED) {
0 ignored issues
show
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1874 can also be of type boolean; however, Doctrine\ORM\UnitOfWork::getEntityState() does only seem to accept object, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
1879 15
                        throw ORMInvalidArgumentException::entityIsRemoved($managedCopy, "merge");
0 ignored issues
show
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1874 can also be of type boolean; however, Doctrine\ORM\ORMInvalidA...tion::entityIsRemoved() does only seem to accept object, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
1880
                    }
1881
                } else {
1882
                    // We need to fetch the managed copy in order to merge.
1883 25
                    $managedCopy = $this->em->find($class->name, $flatId);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

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

Loading history...
1890 1
                        throw EntityNotFoundException::fromClassNameAndIdentifier(
1891 1
                            $class->getName(),
1892 1
                            $this->identifierFlattener->flattenIdentifier($class, $id)
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1893
                        );
1894
                    }
1895
1896 2
                    $managedCopy = $this->newInstance($class);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1897 2
                    $class->setIdentifierValues($managedCopy, $id);
1898
1899 2
                    $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy);
1900 2
                    $this->persistNew($class, $managedCopy);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1901
                } else {
1902 34
                    $this->ensureVersionMatch($class, $entity, $managedCopy);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1874 can also be of type boolean; however, Doctrine\ORM\UnitOfWork::ensureVersionMatch() does only seem to accept object, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
1903 33
                    $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy);
0 ignored issues
show
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1874 can also be of type boolean; however, Doctrine\ORM\UnitOfWork:...yStateIntoManagedCopy() does only seem to accept object, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
1904
                }
1905
            }
1906
1907 40
            $visited[$oid] = $managedCopy; // mark visited
1908
1909 40
            if ($class->isChangeTrackingDeferredExplicit()) {
1910
                $this->scheduleForDirtyCheck($entity);
1911
            }
1912
        }
1913
1914 41
        if ($prevManagedCopy !== null) {
1915 6
            $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy);
0 ignored issues
show
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1874 can also be of type boolean; however, Doctrine\ORM\UnitOfWork:...ationWithMergedEntity() does only seem to accept object, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
1916
        }
1917
1918
        // Mark the managed copy visited as well
1919 41
        $visited[spl_object_hash($managedCopy)] = $managedCopy;
1920
1921 41
        $this->cascadeMerge($entity, $managedCopy, $visited);
0 ignored issues
show
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1874 can also be of type boolean; however, Doctrine\ORM\UnitOfWork::cascadeMerge() does only seem to accept object, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
1922
1923 41
        return $managedCopy;
1924
    }
1925
1926
    /**
1927
     * @param ClassMetadata $class
1928
     * @param object        $entity
1929
     * @param object        $managedCopy
1930
     *
1931
     * @return void
1932
     *
1933
     * @throws OptimisticLockException
1934
     */
1935 34
    private function ensureVersionMatch(ClassMetadata $class, $entity, $managedCopy)
1936
    {
1937 34
        if (! ($class->isVersioned && $this->isLoaded($managedCopy) && $this->isLoaded($entity))) {
1938 31
            return;
1939
        }
1940
1941 4
        $reflField          = $class->reflFields[$class->versionField];
1942 4
        $managedCopyVersion = $reflField->getValue($managedCopy);
1943 4
        $entityVersion      = $reflField->getValue($entity);
1944
1945
        // Throw exception if versions don't match.
1946 4
        if ($managedCopyVersion == $entityVersion) {
1947 3
            return;
1948
        }
1949
1950 1
        throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion);
1951
    }
1952
1953
    /**
1954
     * Tests if an entity is loaded - must either be a loaded proxy or not a proxy
1955
     *
1956
     * @param object $entity
1957
     *
1958
     * @return bool
1959
     */
1960 41
    private function isLoaded($entity)
1961
    {
1962 41
        return !($entity instanceof Proxy) || $entity->__isInitialized();
1963
    }
1964
1965
    /**
1966
     * Sets/adds associated managed copies into the previous entity's association field
1967
     *
1968
     * @param object $entity
1969
     * @param array  $association
1970
     * @param object $previousManagedCopy
1971
     * @param object $managedCopy
1972
     *
1973
     * @return void
1974
     */
1975 6
    private function updateAssociationWithMergedEntity($entity, array $association, $previousManagedCopy, $managedCopy)
1976
    {
1977 6
        $assocField = $association['fieldName'];
1978 6
        $prevClass  = $this->em->getClassMetadata(get_class($previousManagedCopy));
1979
1980 6
        if ($association['type'] & ClassMetadata::TO_ONE) {
1981 6
            $prevClass->reflFields[$assocField]->setValue($previousManagedCopy, $managedCopy);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1982
1983 6
            return;
1984
        }
1985
1986 1
        $value   = $prevClass->reflFields[$assocField]->getValue($previousManagedCopy);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1987 1
        $value[] = $managedCopy;
1988
1989 1
        if ($association['type'] == ClassMetadata::ONE_TO_MANY) {
1990 1
            $class = $this->em->getClassMetadata(get_class($entity));
1991
1992 1
            $class->reflFields[$association['mappedBy']]->setValue($managedCopy, $previousManagedCopy);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1993
        }
1994 1
    }
1995
1996
    /**
1997
     * Detaches an entity from the persistence management. It's persistence will
1998
     * no longer be managed by Doctrine.
1999
     *
2000
     * @param object $entity The entity to detach.
2001
     *
2002
     * @return void
2003
     */
2004 12
    public function detach($entity)
2005
    {
2006 12
        $visited = [];
2007
2008 12
        $this->doDetach($entity, $visited);
2009 12
    }
2010
2011
    /**
2012
     * Executes a detach operation on the given entity.
2013
     *
2014
     * @param object  $entity
2015
     * @param array   $visited
2016
     * @param boolean $noCascade if true, don't cascade detach operation.
2017
     *
2018
     * @return void
2019
     */
2020 16
    private function doDetach($entity, array &$visited, $noCascade = false)
2021
    {
2022 16
        $oid = spl_object_hash($entity);
2023
2024 16
        if (isset($visited[$oid])) {
2025
            return; // Prevent infinite recursion
2026
        }
2027
2028 16
        $visited[$oid] = $entity; // mark visited
2029
2030 16
        switch ($this->getEntityState($entity, self::STATE_DETACHED)) {
2031 16
            case self::STATE_MANAGED:
2032 14
                if ($this->isInIdentityMap($entity)) {
2033 13
                    $this->removeFromIdentityMap($entity);
2034
                }
2035
2036
                unset(
2037 14
                    $this->entityInsertions[$oid],
2038 14
                    $this->entityUpdates[$oid],
2039 14
                    $this->entityDeletions[$oid],
2040 14
                    $this->entityIdentifiers[$oid],
2041 14
                    $this->entityStates[$oid],
2042 14
                    $this->originalEntityData[$oid]
2043
                );
2044 14
                break;
2045 3
            case self::STATE_NEW:
2046 3
            case self::STATE_DETACHED:
2047 3
                return;
2048
        }
2049
2050 14
        if ( ! $noCascade) {
2051 14
            $this->cascadeDetach($entity, $visited);
2052
        }
2053 14
    }
2054
2055
    /**
2056
     * Refreshes the state of the given entity from the database, overwriting
2057
     * any local, unpersisted changes.
2058
     *
2059
     * @param object $entity The entity to refresh.
2060
     *
2061
     * @return void
2062
     *
2063
     * @throws InvalidArgumentException If the entity is not MANAGED.
2064
     */
2065 17
    public function refresh($entity)
2066
    {
2067 17
        $visited = [];
2068
2069 17
        $this->doRefresh($entity, $visited);
2070 17
    }
2071
2072
    /**
2073
     * Executes a refresh operation on an entity.
2074
     *
2075
     * @param object $entity  The entity to refresh.
2076
     * @param array  $visited The already visited entities during cascades.
2077
     *
2078
     * @return void
2079
     *
2080
     * @throws ORMInvalidArgumentException If the entity is not MANAGED.
2081
     */
2082 17
    private function doRefresh($entity, array &$visited)
2083
    {
2084 17
        $oid = spl_object_hash($entity);
2085
2086 17
        if (isset($visited[$oid])) {
2087
            return; // Prevent infinite recursion
2088
        }
2089
2090 17
        $visited[$oid] = $entity; // mark visited
2091
2092 17
        $class = $this->em->getClassMetadata(get_class($entity));
2093
2094 17
        if ($this->getEntityState($entity) !== self::STATE_MANAGED) {
2095
            throw ORMInvalidArgumentException::entityNotManaged($entity);
2096
        }
2097
2098 17
        $this->getEntityPersister($class->name)->refresh(
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2099 17
            array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
2100 17
            $entity
2101
        );
2102
2103 17
        $this->cascadeRefresh($entity, $visited);
2104 17
    }
2105
2106
    /**
2107
     * Cascades a refresh operation to associated entities.
2108
     *
2109
     * @param object $entity
2110
     * @param array  $visited
2111
     *
2112
     * @return void
2113
     */
2114 17 View Code Duplication
    private function cascadeRefresh($entity, array &$visited)
2115
    {
2116 17
        $class = $this->em->getClassMetadata(get_class($entity));
2117
2118 17
        $associationMappings = array_filter(
2119 17
            $class->associationMappings,
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2120
            function ($assoc) { return $assoc['isCascadeRefresh']; }
2121
        );
2122
2123 17
        foreach ($associationMappings as $assoc) {
2124 5
            $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2125
2126
            switch (true) {
2127 5
                case ($relatedEntities instanceof PersistentCollection):
2128
                    // Unwrap so that foreach() does not initialize
2129 5
                    $relatedEntities = $relatedEntities->unwrap();
2130
                    // break; is commented intentionally!
2131
2132
                case ($relatedEntities instanceof Collection):
2133
                case (is_array($relatedEntities)):
2134 5
                    foreach ($relatedEntities as $relatedEntity) {
2135
                        $this->doRefresh($relatedEntity, $visited);
2136
                    }
2137 5
                    break;
2138
2139
                case ($relatedEntities !== null):
2140
                    $this->doRefresh($relatedEntities, $visited);
2141
                    break;
2142
2143 5
                default:
2144
                    // Do nothing
2145
            }
2146
        }
2147 17
    }
2148
2149
    /**
2150
     * Cascades a detach operation to associated entities.
2151
     *
2152
     * @param object $entity
2153
     * @param array  $visited
2154
     *
2155
     * @return void
2156
     */
2157 14 View Code Duplication
    private function cascadeDetach($entity, array &$visited)
2158
    {
2159 14
        $class = $this->em->getClassMetadata(get_class($entity));
2160
2161 14
        $associationMappings = array_filter(
2162 14
            $class->associationMappings,
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2163
            function ($assoc) { return $assoc['isCascadeDetach']; }
2164
        );
2165
2166 14
        foreach ($associationMappings as $assoc) {
2167 3
            $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2168
2169
            switch (true) {
2170 3
                case ($relatedEntities instanceof PersistentCollection):
2171
                    // Unwrap so that foreach() does not initialize
2172 2
                    $relatedEntities = $relatedEntities->unwrap();
2173
                    // break; is commented intentionally!
2174
2175 1
                case ($relatedEntities instanceof Collection):
2176
                case (is_array($relatedEntities)):
2177 3
                    foreach ($relatedEntities as $relatedEntity) {
2178 1
                        $this->doDetach($relatedEntity, $visited);
2179
                    }
2180 3
                    break;
2181
2182
                case ($relatedEntities !== null):
2183
                    $this->doDetach($relatedEntities, $visited);
2184
                    break;
2185
2186 3
                default:
2187
                    // Do nothing
2188
            }
2189
        }
2190 14
    }
2191
2192
    /**
2193
     * Cascades a merge operation to associated entities.
2194
     *
2195
     * @param object $entity
2196
     * @param object $managedCopy
2197
     * @param array  $visited
2198
     *
2199
     * @return void
2200
     */
2201 41
    private function cascadeMerge($entity, $managedCopy, array &$visited)
2202
    {
2203 41
        $class = $this->em->getClassMetadata(get_class($entity));
2204
2205 41
        $associationMappings = array_filter(
2206 41
            $class->associationMappings,
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2207
            function ($assoc) { return $assoc['isCascadeMerge']; }
2208
        );
2209
2210 41
        foreach ($associationMappings as $assoc) {
2211 16
            $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2212
2213 16
            if ($relatedEntities instanceof Collection) {
2214 10
                if ($relatedEntities === $class->reflFields[$assoc['fieldName']]->getValue($managedCopy)) {
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2215 1
                    continue;
2216
                }
2217
2218 9
                if ($relatedEntities instanceof PersistentCollection) {
2219
                    // Unwrap so that foreach() does not initialize
2220 5
                    $relatedEntities = $relatedEntities->unwrap();
2221
                }
2222
2223 9
                foreach ($relatedEntities as $relatedEntity) {
2224 9
                    $this->doMerge($relatedEntity, $visited, $managedCopy, $assoc);
2225
                }
2226 7
            } else if ($relatedEntities !== null) {
2227 15
                $this->doMerge($relatedEntities, $visited, $managedCopy, $assoc);
2228
            }
2229
        }
2230 41
    }
2231
2232
    /**
2233
     * Cascades the save operation to associated entities.
2234
     *
2235
     * @param object $entity
2236
     * @param array  $visited
2237
     *
2238
     * @return void
2239
     */
2240 1067
    private function cascadePersist($entity, array &$visited)
2241
    {
2242 1067
        $class = $this->em->getClassMetadata(get_class($entity));
2243
2244 1067
        $associationMappings = array_filter(
2245 1067
            $class->associationMappings,
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2246
            function ($assoc) { return $assoc['isCascadePersist']; }
2247
        );
2248
2249 1067
        foreach ($associationMappings as $assoc) {
2250 663
            $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2251
2252
            switch (true) {
2253 663
                case ($relatedEntities instanceof PersistentCollection):
2254
                    // Unwrap so that foreach() does not initialize
2255 21
                    $relatedEntities = $relatedEntities->unwrap();
2256
                    // break; is commented intentionally!
2257
2258 663
                case ($relatedEntities instanceof Collection):
2259 602
                case (is_array($relatedEntities)):
2260 565
                    if (($assoc['type'] & ClassMetadata::TO_MANY) <= 0) {
2261 3
                        throw ORMInvalidArgumentException::invalidAssociation(
2262 3
                            $this->em->getClassMetadata($assoc['targetEntity']),
0 ignored issues
show
Compatibility introduced by
$this->em->getClassMetad...$assoc['targetEntity']) of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
2263 3
                            $assoc,
2264 3
                            $relatedEntities
2265
                        );
2266
                    }
2267
2268 562
                    foreach ($relatedEntities as $relatedEntity) {
2269 283
                        $this->doPersist($relatedEntity, $visited);
2270
                    }
2271
2272 562
                    break;
2273
2274 592
                case ($relatedEntities !== null):
2275 253
                    if (! $relatedEntities instanceof $assoc['targetEntity']) {
2276 4
                        throw ORMInvalidArgumentException::invalidAssociation(
2277 4
                            $this->em->getClassMetadata($assoc['targetEntity']),
0 ignored issues
show
Compatibility introduced by
$this->em->getClassMetad...$assoc['targetEntity']) of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
2278 4
                            $assoc,
2279 4
                            $relatedEntities
2280
                        );
2281
                    }
2282
2283 249
                    $this->doPersist($relatedEntities, $visited);
2284 249
                    break;
2285
2286 657
                default:
2287
                    // Do nothing
2288
            }
2289
        }
2290 1060
    }
2291
2292
    /**
2293
     * Cascades the delete operation to associated entities.
2294
     *
2295
     * @param object $entity
2296
     * @param array  $visited
2297
     *
2298
     * @return void
2299
     */
2300 65
    private function cascadeRemove($entity, array &$visited)
2301
    {
2302 65
        $class = $this->em->getClassMetadata(get_class($entity));
2303
2304 65
        $associationMappings = array_filter(
2305 65
            $class->associationMappings,
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2306
            function ($assoc) { return $assoc['isCascadeRemove']; }
2307
        );
2308
2309 65
        $entitiesToCascade = [];
2310
2311 65
        foreach ($associationMappings as $assoc) {
2312 26
            if ($entity instanceof Proxy && !$entity->__isInitialized__) {
0 ignored issues
show
Bug introduced by
Accessing __isInitialized__ on the interface Doctrine\ORM\Proxy\Proxy suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2313 6
                $entity->__load();
2314
            }
2315
2316 26
            $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2317
2318
            switch (true) {
2319 26
                case ($relatedEntities instanceof Collection):
2320 19
                case (is_array($relatedEntities)):
2321
                    // If its a PersistentCollection initialization is intended! No unwrap!
2322 20
                    foreach ($relatedEntities as $relatedEntity) {
2323 10
                        $entitiesToCascade[] = $relatedEntity;
2324
                    }
2325 20
                    break;
2326
2327 19
                case ($relatedEntities !== null):
2328 7
                    $entitiesToCascade[] = $relatedEntities;
2329 7
                    break;
2330
2331 26
                default:
2332
                    // Do nothing
2333
            }
2334
        }
2335
2336 65
        foreach ($entitiesToCascade as $relatedEntity) {
2337 16
            $this->doRemove($relatedEntity, $visited);
2338
        }
2339 65
    }
2340
2341
    /**
2342
     * Acquire a lock on the given entity.
2343
     *
2344
     * @param object $entity
2345
     * @param int    $lockMode
2346
     * @param int    $lockVersion
2347
     *
2348
     * @return void
2349
     *
2350
     * @throws ORMInvalidArgumentException
2351
     * @throws TransactionRequiredException
2352
     * @throws OptimisticLockException
2353
     */
2354 10
    public function lock($entity, $lockMode, $lockVersion = null)
2355
    {
2356 10
        if ($entity === null) {
2357 1
            throw new \InvalidArgumentException("No entity passed to UnitOfWork#lock().");
2358
        }
2359
2360 9
        if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) {
2361 1
            throw ORMInvalidArgumentException::entityNotManaged($entity);
2362
        }
2363
2364 8
        $class = $this->em->getClassMetadata(get_class($entity));
2365
2366
        switch (true) {
2367 8
            case LockMode::OPTIMISTIC === $lockMode:
2368 6
                if ( ! $class->isVersioned) {
0 ignored issues
show
Bug introduced by
Accessing isVersioned on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2370
                }
2371
2372 4
                if ($lockVersion === null) {
2373
                    return;
2374
                }
2375
2376 4
                if ($entity instanceof Proxy && !$entity->__isInitialized__) {
0 ignored issues
show
Bug introduced by
Accessing __isInitialized__ on the interface Doctrine\ORM\Proxy\Proxy suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2377 1
                    $entity->__load();
2378
                }
2379
2380 4
                $entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2381
2382 4
                if ($entityVersion != $lockVersion) {
2383 2
                    throw OptimisticLockException::lockFailedVersionMismatch($entity, $lockVersion, $entityVersion);
2384
                }
2385
2386 2
                break;
2387
2388 2
            case LockMode::NONE === $lockMode:
2389 2
            case LockMode::PESSIMISTIC_READ === $lockMode:
2390 1
            case LockMode::PESSIMISTIC_WRITE === $lockMode:
2391 2
                if (!$this->em->getConnection()->isTransactionActive()) {
2392 2
                    throw TransactionRequiredException::transactionRequired();
2393
                }
2394
2395
                $oid = spl_object_hash($entity);
2396
2397
                $this->getEntityPersister($class->name)->lock(
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2398
                    array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
2399
                    $lockMode
2400
                );
2401
                break;
2402
2403
            default:
2404
                // Do nothing
2405
        }
2406 2
    }
2407
2408
    /**
2409
     * Gets the CommitOrderCalculator used by the UnitOfWork to order commits.
2410
     *
2411
     * @return \Doctrine\ORM\Internal\CommitOrderCalculator
2412
     */
2413 1044
    public function getCommitOrderCalculator()
2414
    {
2415 1044
        return new Internal\CommitOrderCalculator();
2416
    }
2417
2418
    /**
2419
     * Clears the UnitOfWork.
2420
     *
2421
     * @param string|null $entityName if given, only entities of this type will get detached.
2422
     *
2423
     * @return void
2424
     *
2425
     * @throws ORMInvalidArgumentException if an invalid entity name is given
2426
     */
2427 1263
    public function clear($entityName = null)
2428
    {
2429 1263
        if ($entityName === null) {
2430 1261
            $this->identityMap =
2431 1261
            $this->entityIdentifiers =
2432 1261
            $this->originalEntityData =
2433 1261
            $this->entityChangeSets =
2434 1261
            $this->entityStates =
2435 1261
            $this->scheduledForSynchronization =
2436 1261
            $this->entityInsertions =
2437 1261
            $this->entityUpdates =
2438 1261
            $this->entityDeletions =
2439 1261
            $this->nonCascadedNewDetectedEntities =
2440 1261
            $this->collectionDeletions =
2441 1261
            $this->collectionUpdates =
2442 1261
            $this->extraUpdates =
2443 1261
            $this->readOnlyObjects =
2444 1261
            $this->visitedCollections =
2445 1261
            $this->orphanRemovals = [];
2446
        } else {
2447 4
            $this->clearIdentityMapForEntityName($entityName);
2448 4
            $this->clearEntityInsertionsForEntityName($entityName);
2449
        }
2450
2451 1263
        if ($this->evm->hasListeners(Events::onClear)) {
2452 9
            $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em, $entityName));
2453
        }
2454 1263
    }
2455
2456
    /**
2457
     * INTERNAL:
2458
     * Schedules an orphaned entity for removal. The remove() operation will be
2459
     * invoked on that entity at the beginning of the next commit of this
2460
     * UnitOfWork.
2461
     *
2462
     * @ignore
2463
     *
2464
     * @param object $entity
2465
     *
2466
     * @return void
2467
     */
2468 17
    public function scheduleOrphanRemoval($entity)
2469
    {
2470 17
        $this->orphanRemovals[spl_object_hash($entity)] = $entity;
2471 17
    }
2472
2473
    /**
2474
     * INTERNAL:
2475
     * Cancels a previously scheduled orphan removal.
2476
     *
2477
     * @ignore
2478
     *
2479
     * @param object $entity
2480
     *
2481
     * @return void
2482
     */
2483 117
    public function cancelOrphanRemoval($entity)
2484
    {
2485 117
        unset($this->orphanRemovals[spl_object_hash($entity)]);
2486 117
    }
2487
2488
    /**
2489
     * INTERNAL:
2490
     * Schedules a complete collection for removal when this UnitOfWork commits.
2491
     *
2492
     * @param PersistentCollection $coll
2493
     *
2494
     * @return void
2495
     */
2496 14
    public function scheduleCollectionDeletion(PersistentCollection $coll)
2497
    {
2498 14
        $coid = spl_object_hash($coll);
2499
2500
        // TODO: if $coll is already scheduled for recreation ... what to do?
2501
        // Just remove $coll from the scheduled recreations?
2502 14
        unset($this->collectionUpdates[$coid]);
2503
2504 14
        $this->collectionDeletions[$coid] = $coll;
2505 14
    }
2506
2507
    /**
2508
     * @param PersistentCollection $coll
2509
     *
2510
     * @return bool
2511
     */
2512
    public function isCollectionScheduledForDeletion(PersistentCollection $coll)
2513
    {
2514
        return isset($this->collectionDeletions[spl_object_hash($coll)]);
2515
    }
2516
2517
    /**
2518
     * @param ClassMetadata $class
2519
     *
2520
     * @return \Doctrine\Common\Persistence\ObjectManagerAware|object
2521
     */
2522 700
    private function newInstance($class)
2523
    {
2524 700
        $entity = $class->newInstance();
2525
2526 700
        if ($entity instanceof \Doctrine\Common\Persistence\ObjectManagerAware) {
2527 4
            $entity->injectObjectManager($this->em, $class);
2528
        }
2529
2530 700
        return $entity;
2531
    }
2532
2533
    /**
2534
     * INTERNAL:
2535
     * Creates an entity. Used for reconstitution of persistent entities.
2536
     *
2537
     * Internal note: Highly performance-sensitive method.
2538
     *
2539
     * @ignore
2540
     *
2541
     * @param string $className The name of the entity class.
2542
     * @param array  $data      The data for the entity.
2543
     * @param array  $hints     Any hints to account for during reconstitution/lookup of the entity.
2544
     *
2545
     * @return object The managed entity instance.
2546
     *
2547
     * @deprecated this method is obsolete and will be removed in ORM v3
2548
     */
2549 842
    public function createEntity($className, array $data, &$hints = [])
2550
    {
2551 842
        return $this->getOrCreateEntity($className, $data, $hints)[0];
2552
    }
2553
2554 842
    /**
2555 842
     * @internal Creates an entity. Used for reconstitution of persistent entities.
2556
     *
2557 842
     * Internal note: Highly performance-sensitive method.
2558 324
     *
2559 324
     * @ignore
2560
     *
2561
     * @param string $className The name of the entity class.
2562 324
     * @param array  $data      The data for the entity.
2563 324
     * @param array  $hints     Any hints to account for during reconstitution/lookup of the entity.
2564 324
     *
2565 324
     * @return object[]|bool[] a tuple containing the managed entity (first key) and whether the
2566 324
     *                         returned entity can be hydrated or not
2567
     */
2568
    public function getOrCreateEntity($className, array $data, &$hints = []) : array
2569
    {
2570
        $class = $this->em->getClassMetadata($className);
2571
        //$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...
2572 2
2573 2
        $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...
2574
        $idHash = implode(' ', $id);
2575
2576 2
        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...
2577
            $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...
2578
            $oid = spl_object_hash($entity);
2579 322
2580 23
            if (
2581
                isset($hints[Query::HINT_REFRESH])
2582 23
                && isset($hints[Query::HINT_REFRESH_ENTITY])
2583
                && ($unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY]) !== $entity
2584 23
                && $unmanagedProxy instanceof Proxy
2585 23
                && $this->isIdentifierEquals($unmanagedProxy, $entity)
2586
            ) {
2587
                // DDC-1238 - we have a managed instance, but it isn't the provided one.
2588 301
                // Therefore we clear its identifier. Also, we must re-fetch metadata since the
2589
                // refreshed object may be anything
2590
2591 301
                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...
2592 72
                    $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...
2593
                }
2594
2595
                return [$unmanagedProxy, false];
2596 322
            }
2597
2598 115
            if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
2599 3
                $entity->__setInitialized(true);
2600
2601
                $overrideLocalValues = true;
2602 322
2603
                if ($entity instanceof NotifyPropertyChanged) {
2604
                    $entity->addPropertyChangedListener($this);
2605 695
                }
2606 695
            } else {
2607
                $overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
2608 695
2609 695
                // If only a specific entity is set to refresh, check that it's the one
2610 695
                if (isset($hints[Query::HINT_REFRESH_ENTITY])) {
2611
                    $overrideLocalValues = $hints[Query::HINT_REFRESH_ENTITY] === $entity;
2612 695
                }
2613
            }
2614 695
2615 2
            if ($overrideLocalValues) {
2616
                // inject ObjectManager upon refresh.
2617
                if ($entity instanceof ObjectManagerAware) {
2618 695
                    $entity->injectObjectManager($this->em, $class);
2619
                }
2620
2621 841
                $this->originalEntityData[$oid] = $data;
2622 230
            }
2623
        } else {
2624
            $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...
2625 733
            $oid    = spl_object_hash($entity);
2626 733
2627 733
            $this->entityIdentifiers[$oid]  = $id;
2628
            $this->entityStates[$oid]       = self::STATE_MANAGED;
2629
            $this->originalEntityData[$oid] = $data;
2630
2631
            $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...
2632 733
2633
            if ($entity instanceof NotifyPropertyChanged) {
2634 733
                $entity->addPropertyChangedListener($this);
2635
            }
2636
2637
            $overrideLocalValues = true;
2638
        }
2639 733
2640 34
        if ( ! $overrideLocalValues) {
2641
            return [$entity, false];
2642
        }
2643 699
2644
        foreach ($data as $field => $value) {
2645 603
            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...
2646 259
                $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...
2647
            }
2648
        }
2649 579
2650
        // Loading the entity right here, if its in the eager loading map get rid of it there.
2651
        unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
2652 579
2653 499
        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...
2654
            unset($this->eagerLoadingEntities[$class->rootEntityName]);
2655
        }
2656 67
2657
        // Properly initialize any unfetched associations, if partial objects are not allowed.
2658 3
        if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
2659
            return [$entity, true];
2660 3
        }
2661 3
2662
        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...
2663 3
            // Check if the association is not among the fetch-joined associations already.
2664
            if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) {
2665
                continue;
2666
            }
2667 64
2668
            $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
2669 64
2670
            switch (true) {
2671
                case ($assoc['type'] & ClassMetadata::TO_ONE):
2672
                    if ( ! $assoc['isOwningSide']) {
2673 499
2674 38
                        // use the given entity association
2675 38
                        if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2676
2677 38
                            $this->originalEntityData[$oid][$field] = $data[$field];
2678
2679
                            $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...
2680 492
                            $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...
2681
2682
                            continue 2;
2683 492
                        }
2684 492
2685
                        // Inverse side of x-to-one can never be lazy
2686 492
                        $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...
2687 296
2688 11
                        continue 2;
2689
                    }
2690 296
2691
                    // use the entity association
2692 289
                    if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2693 289
                        $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...
2694
                        $this->originalEntityData[$oid][$field] = $data[$field];
2695
2696 7
                        continue;
2697 492
                    }
2698
2699
                    $associatedId = [];
2700
2701 492
                    // TODO: Is this even computed right in all cases of composite keys?
2702
                    foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
2703 289
                        $joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
2704 289
2705
                        if ($joinColumnValue !== null) {
2706 289
                            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...
2707
                                $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
2708
                            } else {
2709 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...
2710 293
                            }
2711
                        } 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...
2712
                            && 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...
2713
                        ) {
2714
                            // the missing key is part of target's entity primary key
2715
                            $associatedId = [];
2716
                            break;
2717 296
                        }
2718
                    }
2719
2720 296
                    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...
2721 170
                        // Foreign key is NULL
2722
                        $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...
2723
                        $this->originalEntityData[$oid][$field] = null;
2724
2725
                        continue;
2726 170
                    }
2727 170
2728 170
                    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...
2729 170
                        $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...
2730 170
                    }
2731
2732
                    // Foreign key is set
2733
                    // Check identity map first
2734
                    // FIXME: Can break easily with composite keys if join column values are in
2735 170
                    //        wrong order. The correct order is the one in ClassMetadata#identifier.
2736
                    $relatedIdHash = implode(' ', $associatedId);
2737 202
2738
                    switch (true) {
2739
                        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...
2740
                            $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...
2741 32
2742 32
                            // If this is an uninitialized proxy, we are deferring eager loads,
2743
                            // this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
2744
                            // then we can append this entity for eager loading!
2745
                            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...
2746
                                isset($hints[self::HINT_DEFEREAGERLOAD]) &&
2747 172
                                !$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...
2748 165
                                $newValue instanceof Proxy &&
2749 165
                                $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...
2750
2751
                                $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...
2752 7
                            }
2753
2754 7
                            break;
2755
2756 7
                        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...
2757 7
                            // If it might be a subtype, it can not be lazy. There isn't even
2758
                            // a way to solve this with deferred eager loading, which means putting
2759
                            // an entity with subclasses at a *-to-one location is really bad! (performance-wise)
2760
                            $newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, $associatedId);
2761
                            break;
2762
2763
                        default:
2764
                            switch (true) {
2765
                                // We are negating the condition here. Other cases will assume it is valid!
2766 172
                                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...
2767 172
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2768 172
                                    break;
2769
2770
                                // Deferred eager load only works for single identifier classes
2771 172
                                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...
2772 172
                                    // TODO: Is there a faster approach?
2773
                                    $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...
2774
2775
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2776 172
                                    break;
2777
2778 172
                                default:
2779
                                    // TODO: This is very imperformant, ignore it?
2780
                                    $newValue = $this->em->find($assoc['targetEntity'], $associatedId);
2781 296
                                    break;
2782 296
                            }
2783
2784 296
                            // PERF: Inlined & optimized code from UnitOfWork#registerManaged()
2785 57
                            $newValueOid = spl_object_hash($newValue);
2786 57
                            $this->entityIdentifiers[$newValueOid] = $associatedId;
2787
                            $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...
2788
2789 296
                            if (
2790
                                $newValue instanceof NotifyPropertyChanged &&
2791
                                ( ! $newValue instanceof Proxy || $newValue->__isInitialized())
2792
                            ) {
2793 492
                                $newValue->addPropertyChangedListener($this);
2794
                            }
2795
                            $this->entityStates[$newValueOid] = self::STATE_MANAGED;
2796
                            // make sure that when an proxy is then finally loaded, $this->originalEntityData is set also!
2797
                            break;
2798 492
                    }
2799
2800 3
                    $this->originalEntityData[$oid][$field] = $newValue;
2801
                    $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...
2802 3
2803 3
                    if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
2804
                        $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...
2805 3
                        $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...
2806
                    }
2807
2808
                    break;
2809 492
2810 492
                default:
2811 492
                    // Ignore if its a cached collection
2812
                    if (isset($hints[Query::HINT_CACHE_ENABLED]) && $class->getFieldValue($entity, $field) instanceof PersistentCollection) {
2813 492
                        break;
2814 492
                    }
2815
2816 492
                    // use the given collection
2817 4
                    if (isset($data[$field]) && $data[$field] instanceof PersistentCollection) {
2818 4
2819
                        $data[$field]->setOwner($entity, $assoc);
2820
2821 492
                        $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...
2822 579
                        $this->originalEntityData[$oid][$field] = $data[$field];
2823
2824
                        break;
2825
                    }
2826 699
2827
                    // Inject collection
2828 699
                    $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...
2829
                    $pColl->setOwner($entity, $assoc);
2830
                    $pColl->setInitialized(false);
2831 699
2832
                    $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...
2833
                    $reflField->setValue($entity, $pColl);
2834
2835
                    if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
2836
                        $this->loadCollection($pColl);
2837 907
                        $pColl->takeSnapshot();
2838
                    }
2839 907
2840 907
                    $this->originalEntityData[$oid][$field] = $pColl;
2841
                    break;
2842
            }
2843
        }
2844 7
2845 7
        if ($overrideLocalValues) {
2846
            // defer invoking of postLoad event to hydration complete step
2847 7
            $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...
2848 7
        }
2849
2850
        return [$entity, true];
2851
    }
2852 7
2853
    /**
2854 7
     * @return void
2855 7
     */
2856
    public function triggerEagerLoads()
2857
    {
2858 7
        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...
2859
            return;
2860
        }
2861
2862
        // avoid infinite recursion
2863
        $eagerLoadingEntities       = $this->eagerLoadingEntities;
2864
        $this->eagerLoadingEntities = [];
2865
2866
        foreach ($eagerLoadingEntities as $entityName => $ids) {
2867
            if ( ! $ids) {
2868
                continue;
2869 147
            }
2870
2871 147
            $class = $this->em->getClassMetadata($entityName);
2872 147
2873
            $this->getEntityPersister($entityName)->loadAll(
2874 147
                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...
2875 147
            );
2876 77
        }
2877 77
    }
2878
2879 84
    /**
2880 84
     * Initializes (loads) an uninitialized persistent collection of an entity.
2881 84
     *
2882
     * @param \Doctrine\ORM\PersistentCollection $collection The collection to initialize.
2883
     *
2884 147
     * @return void
2885 147
     *
2886
     * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733.
2887
     */
2888
    public function loadCollection(PersistentCollection $collection)
2889
    {
2890
        $assoc     = $collection->getMapping();
2891
        $persister = $this->getEntityPersister($assoc['targetEntity']);
2892 2
2893
        switch ($assoc['type']) {
2894 2
            case ClassMetadata::ONE_TO_MANY:
2895
                $persister->loadOneToManyCollection($assoc, $collection->getOwner(), $collection);
2896
                break;
2897
2898
            case ClassMetadata::MANY_TO_MANY:
2899
                $persister->loadManyToManyCollection($assoc, $collection->getOwner(), $collection);
2900
                break;
2901
        }
2902
2903
        $collection->setInitialized(true);
2904
    }
2905 121
2906
    /**
2907 121
     * Gets the identity map of the UnitOfWork.
2908
     *
2909 121
     * @return array
2910 117
     */
2911 121
    public function getIdentityMap()
2912
    {
2913
        return $this->identityMap;
2914
    }
2915
2916
    /**
2917
     * Gets the original data of an entity. The original data is the data that was
2918
     * present at the time the entity was reconstituted from the database.
2919
     *
2920
     * @param object $entity
2921
     *
2922
     * @return array
2923
     */
2924
    public function getOriginalEntityData($entity)
2925
    {
2926
        $oid = spl_object_hash($entity);
2927
2928
        return isset($this->originalEntityData[$oid])
2929
            ? $this->originalEntityData[$oid]
2930
            : [];
2931
    }
2932
2933
    /**
2934
     * @ignore
2935
     *
2936
     * @param object $entity
2937
     * @param array  $data
2938
     *
2939 313
     * @return void
2940
     */
2941 313
    public function setOriginalEntityData($entity, array $data)
2942 313
    {
2943
        $this->originalEntityData[spl_object_hash($entity)] = $data;
2944
    }
2945
2946
    /**
2947
     * INTERNAL:
2948
     * Sets a property value of the original data array of an entity.
2949
     *
2950
     * @ignore
2951
     *
2952
     * @param string $oid
2953
     * @param string $property
2954 861
     * @param mixed  $value
2955
     *
2956 861
     * @return void
2957
     */
2958
    public function setOriginalEntityProperty($oid, $property, $value)
2959
    {
2960
        $this->originalEntityData[$oid][$property] = $value;
2961
    }
2962
2963
    /**
2964
     * Gets the identifier of an entity.
2965
     * The returned value is always an array of identifier values. If the entity
2966
     * has a composite identifier then the identifier values are in the same
2967
     * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames().
2968 128
     *
2969
     * @param object $entity
2970 128
     *
2971
     * @return array The identifier values.
2972 128
     */
2973
    public function getEntityIdentifier($entity)
2974
    {
2975
        return $this->entityIdentifiers[spl_object_hash($entity)];
2976 128
    }
2977 115
2978 128
    /**
2979
     * Processes an entity instance to extract their identifier values.
2980 128
     *
2981
     * @param object $entity The entity instance.
2982
     *
2983
     * @return mixed A scalar value.
2984
     *
2985
     * @throws \Doctrine\ORM\ORMInvalidArgumentException
2986
     */
2987
    public function getSingleIdentifierValue($entity)
2988
    {
2989
        $class = $this->em->getClassMetadata(get_class($entity));
2990
2991
        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...
2992
            throw ORMInvalidArgumentException::invalidCompositeIdentifier();
2993 546
        }
2994
2995 546
        $values = $this->isInIdentityMap($entity)
2996
            ? $this->getEntityIdentifier($entity)
2997 546
            : $class->getIdentifierValues($entity);
2998 86
2999 546
        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...
3000
    }
3001
3002
    /**
3003
     * Tries to find an entity with the given identifier in the identity map of
3004
     * this UnitOfWork.
3005
     *
3006
     * @param mixed  $id            The entity identifier to look for.
3007
     * @param string $rootClassName The name of the root class of the mapped entity hierarchy.
3008
     *
3009
     * @return object|bool Returns the entity with the specified identifier if it exists in
3010
     *                     this UnitOfWork, FALSE otherwise.
3011 5
     */
3012 View Code Duplication
    public function tryGetById($id, $rootClassName)
3013 5
    {
3014
        $idHash = implode(' ', (array) $id);
3015 5
3016 5
        return isset($this->identityMap[$rootClassName][$idHash])
3017
            ? $this->identityMap[$rootClassName][$idHash]
3018
            : false;
3019
    }
3020
3021
    /**
3022
     * Schedules an entity for dirty-checking at commit-time.
3023
     *
3024
     * @param object $entity The entity to schedule for dirty-checking.
3025
     *
3026
     * @return void
3027
     *
3028
     * @todo Rename: scheduleForSynchronization
3029
     */
3030
    public function scheduleForDirtyCheck($entity)
3031
    {
3032
        $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...
3033
3034 1
        $this->scheduledForSynchronization[$rootClassName][spl_object_hash($entity)] = $entity;
3035
    }
3036 1
3037
    /**
3038 1
     * Checks whether the UnitOfWork has any pending insertions.
3039
     *
3040
     * @return boolean TRUE if this UnitOfWork has pending insertions, FALSE otherwise.
3041
     */
3042
    public function hasPendingInsertions()
3043
    {
3044
        return ! empty($this->entityInsertions);
3045
    }
3046
3047
    /**
3048 1109
     * Calculates the size of the UnitOfWork. The size of the UnitOfWork is the
3049
     * number of entities in the identity map.
3050 1109
     *
3051 871
     * @return integer
3052
     */
3053
    public function size()
3054 1109
    {
3055
        $countArray = array_map('count', $this->identityMap);
3056
3057 1109
        return array_sum($countArray);
3058 1063
    }
3059 1063
3060
    /**
3061 379
     * Gets the EntityPersister for an Entity.
3062 225
     *
3063 225
     * @param string $entityName The name of the Entity.
3064
     *
3065 347
     * @return \Doctrine\ORM\Persisters\Entity\EntityPersister
3066 347
     */
3067 347
    public function getEntityPersister($entityName)
3068
    {
3069
        if (isset($this->persisters[$entityName])) {
3070
            return $this->persisters[$entityName];
3071
        }
3072
3073 1109
        $class = $this->em->getClassMetadata($entityName);
3074 125
3075 125
        switch (true) {
3076 125
            case ($class->isInheritanceTypeNone()):
3077 125
                $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...
3078
                break;
3079
3080 1109
            case ($class->isInheritanceTypeSingleTable()):
3081
                $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...
3082 1109
                break;
3083
3084
            case ($class->isInheritanceTypeJoined()):
3085
                $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...
3086
                break;
3087
3088
            default:
3089
                throw new \RuntimeException('No persister found for entity.');
3090
        }
3091
3092 574
        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...
3093
            $persister = $this->em->getConfiguration()
3094 574
                ->getSecondLevelCacheConfiguration()
3095 78
                ->getCacheFactory()
3096 574
                ->buildCachedEntityPersister($this->em, $persister, $class);
3097
        }
3098 574
3099 452
        $this->persisters[$entityName] = $persister;
3100
3101
        return $this->persisters[$entityName];
3102 574
    }
3103 408
3104 574
    /**
3105
     * Gets a collection persister for a collection-valued association.
3106 574
     *
3107 77
     * @param array $association
3108 77
     *
3109 77
     * @return \Doctrine\ORM\Persisters\Collection\CollectionPersister
3110 77
     */
3111
    public function getCollectionPersister(array $association)
3112
    {
3113 574
        $role = isset($association['cache'])
3114
            ? $association['sourceEntity'] . '::' . $association['fieldName']
3115 574
            : $association['type'];
3116
3117
        if (isset($this->collectionPersisters[$role])) {
3118
            return $this->collectionPersisters[$role];
3119
        }
3120
3121
        $persister = ClassMetadata::ONE_TO_MANY === $association['type']
3122
            ? new OneToManyPersister($this->em)
3123
            : new ManyToManyPersister($this->em);
3124
3125
        if ($this->hasCache && isset($association['cache'])) {
3126
            $persister = $this->em->getConfiguration()
3127
                ->getSecondLevelCacheConfiguration()
3128 209
                ->getCacheFactory()
3129
                ->buildCachedCollectionPersister($this->em, $persister, $association);
3130 209
        }
3131
3132 209
        $this->collectionPersisters[$role] = $persister;
3133 209
3134 209
        return $this->collectionPersisters[$role];
3135
    }
3136 209
3137
    /**
3138 203
     * INTERNAL:
3139 2
     * Registers an entity as managed.
3140
     *
3141 203
     * @param object $entity The entity.
3142
     * @param array  $id     The identifier values.
3143
     * @param array  $data   The original entity data.
3144
     *
3145
     * @return void
3146
     */
3147
    public function registerManaged($entity, array $id, array $data)
3148
    {
3149
        $oid = spl_object_hash($entity);
3150
3151 15
        $this->entityIdentifiers[$oid]  = $id;
3152
        $this->entityStates[$oid]       = self::STATE_MANAGED;
3153 15
        $this->originalEntityData[$oid] = $data;
3154 15
3155
        $this->addToIdentityMap($entity);
3156
3157
        if ($entity instanceof NotifyPropertyChanged && ( ! $entity instanceof Proxy || $entity->__isInitialized())) {
3158
            $entity->addPropertyChangedListener($this);
3159
        }
3160
    }
3161
3162
    /**
3163
     * INTERNAL:
3164
     * Clears the property changeset of the entity with the given OID.
3165
     *
3166
     * @param string $oid The entity's OID.
3167
     *
3168 3
     * @return void
3169
     */
3170 3
    public function clearEntityChangeSet($oid)
3171 3
    {
3172
        unset($this->entityChangeSets[$oid]);
3173 3
    }
3174
3175 3
    /* PropertyChangedListener implementation */
3176 1
3177
    /**
3178
     * Notifies this UnitOfWork of a property change in an entity.
3179
     *
3180 3
     * @param object $entity       The entity that owns the property.
3181
     * @param string $propertyName The name of the property that changed.
3182 3
     * @param mixed  $oldValue     The old value of the property.
3183 3
     * @param mixed  $newValue     The new value of the property.
3184
     *
3185 3
     * @return void
3186
     */
3187
    public function propertyChanged($entity, $propertyName, $oldValue, $newValue)
3188
    {
3189
        $oid   = spl_object_hash($entity);
3190
        $class = $this->em->getClassMetadata(get_class($entity));
3191
3192 2
        $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...
3193
3194 2
        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...
3195
            return; // ignore non-persistent fields
3196
        }
3197
3198
        // Update changeset and mark entity for synchronization
3199
        $this->entityChangeSets[$oid][$propertyName] = [$oldValue, $newValue];
3200
3201
        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...
3202 3
            $this->scheduleForDirtyCheck($entity);
3203
        }
3204 3
    }
3205
3206
    /**
3207
     * Gets the currently scheduled entity insertions in this UnitOfWork.
3208
     *
3209
     * @return array
3210
     */
3211
    public function getScheduledEntityInsertions()
3212 1
    {
3213
        return $this->entityInsertions;
3214 1
    }
3215
3216
    /**
3217
     * Gets the currently scheduled entity updates in this UnitOfWork.
3218
     *
3219
     * @return array
3220
     */
3221
    public function getScheduledEntityUpdates()
3222 1
    {
3223
        return $this->entityUpdates;
3224 1
    }
3225
3226
    /**
3227
     * Gets the currently scheduled entity deletions in this UnitOfWork.
3228
     *
3229
     * @return array
3230
     */
3231
    public function getScheduledEntityDeletions()
3232
    {
3233
        return $this->entityDeletions;
3234
    }
3235
3236
    /**
3237
     * Gets the currently scheduled complete collection deletions
3238
     *
3239
     * @return array
3240
     */
3241
    public function getScheduledCollectionDeletions()
3242
    {
3243
        return $this->collectionDeletions;
3244 2
    }
3245
3246 2
    /**
3247 1
     * Gets the currently scheduled collection inserts, updates and deletes.
3248
     *
3249 1
     * @return array
3250
     */
3251
    public function getScheduledCollectionUpdates()
3252 1
    {
3253 1
        return $this->collectionUpdates;
3254
    }
3255 1
3256
    /**
3257
     * Helper method to initialize a lazy loading proxy or persistent collection.
3258
     *
3259
     * @param object $obj
3260
     *
3261
     * @return void
3262
     */
3263
    public function initializeObject($obj)
3264 1
    {
3265
        if ($obj instanceof Proxy) {
3266 1
            $obj->__load();
3267
3268
            return;
3269
        }
3270
3271
        if ($obj instanceof PersistentCollection) {
3272
            $obj->initialize();
3273
        }
3274
    }
3275
3276
    /**
3277
     * Helper method to show an object as string.
3278
     *
3279
     * @param object $obj
3280
     *
3281 6
     * @return string
3282
     */
3283 6
    private static function objToStr($obj)
3284 1
    {
3285
        return method_exists($obj, '__toString') ? (string) $obj : get_class($obj).'@'.spl_object_hash($obj);
3286
    }
3287 5
3288 5
    /**
3289
     * Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit().
3290
     *
3291
     * This operation cannot be undone as some parts of the UnitOfWork now keep gathering information
3292
     * on this object that might be necessary to perform a correct update.
3293
     *
3294
     * @param object $object
3295
     *
3296
     * @return void
3297
     *
3298
     * @throws ORMInvalidArgumentException
3299 3
     */
3300
    public function markReadOnly($object)
3301 3
    {
3302
        if ( ! is_object($object) || ! $this->isInIdentityMap($object)) {
3303
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3304
        }
3305 3
3306
        $this->readOnlyObjects[spl_object_hash($object)] = true;
3307
    }
3308
3309
    /**
3310
     * Is this entity read only?
3311
     *
3312
     * @param object $object
3313 1039
     *
3314 94
     * @return bool
3315 1039
     *
3316 1039
     * @throws ORMInvalidArgumentException
3317
     */
3318
    public function isReadOnly($object)
3319
    {
3320
        if ( ! is_object($object)) {
3321
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3322
        }
3323 11
3324 3
        return isset($this->readOnlyObjects[spl_object_hash($object)]);
3325 11
    }
3326 11
3327
    /**
3328
     * Perform whatever processing is encapsulated here after completion of the transaction.
3329
     */
3330
    private function afterTransactionComplete()
3331
    {
3332
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3333 1044
            $persister->afterTransactionComplete();
3334
        });
3335 1044
    }
3336 950
3337
    /**
3338
     * Perform whatever processing is encapsulated here after completion of the rolled-back.
3339 94
     */
3340 94
    private function afterTransactionRolledBack()
3341 94
    {
3342
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3343
            $persister->afterTransactionRolledBack();
3344 94
        });
3345
    }
3346 1048
3347
    /**
3348 1048
     * Performs an action after the transaction.
3349 4
     *
3350
     * @param callable $callback
3351 1048
     */
3352
    private function performCallbackOnCachedPersister(callable $callback)
3353 1043
    {
3354
        if ( ! $this->hasCache) {
3355 1043
            return;
3356 5
        }
3357
3358 1042
        foreach (array_merge($this->persisters, $this->collectionPersisters) as $persister) {
3359
            if ($persister instanceof CachedPersister) {
3360
                $callback($persister);
3361
            }
3362
        }
3363
    }
3364
3365
    private function dispatchOnFlushEvent()
3366
    {
3367
        if ($this->evm->hasListeners(Events::onFlush)) {
3368 14
            $this->evm->dispatchEvent(Events::onFlush, new OnFlushEventArgs($this->em));
3369
        }
3370 14
    }
3371
3372
    private function dispatchPostFlushEvent()
3373
    {
3374 14
        if ($this->evm->hasListeners(Events::postFlush)) {
3375
            $this->evm->dispatchEvent(Events::postFlush, new PostFlushEventArgs($this->em));
3376 14
        }
3377 11
    }
3378
3379
    /**
3380 3
     * Verifies if two given entities actually are the same based on identifier comparison
3381 3
     *
3382
     * @param object $entity1
3383 3
     * @param object $entity2
3384 3
     *
3385 3
     * @return bool
3386 3
     */
3387 3
    private function isIdentifierEquals($entity1, $entity2)
3388 3
    {
3389
        if ($entity1 === $entity2) {
3390 3
            return true;
3391
        }
3392
3393
        $class = $this->em->getClassMetadata(get_class($entity1));
3394
3395
        if ($class !== $this->em->getClassMetadata(get_class($entity2))) {
3396 1046
            return false;
3397
        }
3398 1046
3399
        $oid1 = spl_object_hash($entity1);
3400 1046
        $oid2 = spl_object_hash($entity2);
3401
3402 1046
        $id1 = isset($this->entityIdentifiers[$oid1])
3403 5
            ? $this->entityIdentifiers[$oid1]
3404 5
            : $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...
3405
        $id2 = isset($this->entityIdentifiers[$oid2])
3406
            ? $this->entityIdentifiers[$oid2]
3407 1044
            : $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...
3408
3409
        return $id1 === $id2 || implode(' ', $id1) === implode(' ', $id2);
3410
    }
3411
3412
    /**
3413
     * @throws ORMInvalidArgumentException
3414
     */
3415
    private function assertThatThereAreNoUnintentionallyNonPersistedAssociations() : void
3416
    {
3417 40
        $entitiesNeedingCascadePersist = \array_diff_key($this->nonCascadedNewDetectedEntities, $this->entityInsertions);
3418
3419 40
        $this->nonCascadedNewDetectedEntities = [];
3420 7
3421
        if ($entitiesNeedingCascadePersist) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $entitiesNeedingCascadePersist of type array<object|array>[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
3422
            throw ORMInvalidArgumentException::newEntitiesFoundThroughRelationships(
3423 33
                \array_values($entitiesNeedingCascadePersist)
3424 4
            );
3425
        }
3426
    }
3427 33
3428
    /**
3429 33
     * @param object $entity
3430 33
     * @param object $managedCopy
3431
     *
3432 33
     * @throws ORMException
3433
     * @throws OptimisticLockException
3434 33
     * @throws TransactionRequiredException
3435 33
     */
3436 33
    private function mergeEntityStateIntoManagedCopy($entity, $managedCopy)
3437
    {
3438
        if (! $this->isLoaded($entity)) {
3439 29
            return;
3440
        }
3441 29
3442 25
        if (! $this->isLoaded($managedCopy)) {
3443 25
            $managedCopy->__load();
3444 12
        }
3445
3446 16
        $class = $this->em->getClassMetadata(get_class($entity));
3447
3448 4
        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...
3449
            $name = $prop->name;
3450
3451 12
            $prop->setAccessible(true);
3452 6
3453 3
            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...
3454 3
                if ( ! $class->isIdentifier($name)) {
3455
                    $prop->setValue($managedCopy, $prop->getValue($entity));
3456 3
                }
3457 2
            } else {
3458
                $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...
3459 1
3460 1
                if ($assoc2['type'] & ClassMetadata::TO_ONE) {
3461 1
                    $other = $prop->getValue($entity);
3462
                    if ($other === null) {
3463 1
                        $prop->setValue($managedCopy, null);
3464
                    } else {
3465
                        if ($other instanceof Proxy && !$other->__isInitialized()) {
3466
                            // do not merge fields marked lazy that have not been fetched.
3467 21
                            continue;
3468
                        }
3469
3470
                        if ( ! $assoc2['isCascadeMerge']) {
3471 17
                            if ($this->getEntityState($other) === self::STATE_DETACHED) {
3472
                                $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']);
3473 17
                                $relatedId   = $targetClass->getIdentifierValues($other);
3474
3475
                                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...
3476 5
                                    $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...
3477
                                } else {
3478
                                    $other = $this->em->getProxyFactory()->getProxy(
3479 14
                                        $assoc2['targetEntity'],
3480
                                        $relatedId
3481 14
                                    );
3482 4
                                    $this->registerManaged($other, $relatedId, []);
3483 4
                                }
3484 4
                            }
3485 4
3486
                            $prop->setValue($managedCopy, $other);
3487 4
                        }
3488 4
                    }
3489
                } else {
3490
                    $mergeCol = $prop->getValue($entity);
3491 14
3492 9
                    if ($mergeCol instanceof PersistentCollection && ! $mergeCol->isInitialized()) {
3493
                        // do not merge fields marked lazy that have not been fetched.
3494
                        // keep the lazy persistent collection of the managed copy.
3495 9
                        continue;
3496 1
                    }
3497 1
3498
                    $managedCol = $prop->getValue($managedCopy);
3499 1
3500 1
                    if ( ! $managedCol) {
3501 1
                        $managedCol = new PersistentCollection(
3502
                            $this->em,
3503
                            $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...
3504
                            new ArrayCollection
3505
                        );
3506
                        $managedCol->setOwner($managedCopy, $assoc2);
3507
                        $prop->setValue($managedCopy, $managedCol);
3508
                    }
3509
3510 33
                    if ($assoc2['isCascadeMerge']) {
3511
                        $managedCol->initialize();
3512 33
3513
                        // clear and set dirty a managed collection if its not also the same collection to merge from.
3514
                        if ( ! $managedCol->isEmpty() && $managedCol !== $mergeCol) {
3515 33
                            $managedCol->unwrap()->clear();
3516
                            $managedCol->setDirty(true);
3517
3518
                            if ($assoc2['isOwningSide']
3519
                                && $assoc2['type'] == ClassMetadata::MANY_TO_MANY
3520
                                && $class->isChangeTrackingNotify()
3521
                            ) {
3522
                                $this->scheduleForDirtyCheck($managedCopy);
3523 921
                            }
3524
                        }
3525 921
                    }
3526 921
                }
3527
            }
3528
3529
            if ($class->isChangeTrackingNotify()) {
3530
                // Just treat all properties as changed, there is no other choice.
3531 4
                $this->propertyChanged($managedCopy, $name, null, $prop->getValue($managedCopy));
3532
            }
3533 4
        }
3534
    }
3535
3536
    /**
3537 4
     * This method called by hydrators, and indicates that hydrator totally completed current hydration cycle.
3538
     * Unit of work able to fire deferred events, related to loading events here.
3539 4
     *
3540 4
     * @internal should be called internally from object hydrators
3541
     */
3542 4
    public function hydrationComplete()
3543
    {
3544
        $this->hydrationCompleteHandler->hydrationComplete();
3545
    }
3546
3547 4
    /**
3548
     * @param string $entityName
3549 4
     */
3550
    private function clearIdentityMapForEntityName($entityName)
3551 1
    {
3552 1
        if (! isset($this->identityMap[$entityName])) {
3553
            return;
3554
        }
3555 4
3556
        $visited = [];
3557
3558
        foreach ($this->identityMap[$entityName] as $entity) {
3559
            $this->doDetach($entity, $visited, false);
3560
        }
3561
    }
3562
3563
    /**
3564
     * @param string $entityName
3565 945
     */
3566
    private function clearEntityInsertionsForEntityName($entityName)
3567 945
    {
3568 945
        foreach ($this->entityInsertions as $hash => $entity) {
3569 945
            // note: performance optimization - `instanceof` is much faster than a function call
3570
            if ($entity instanceof $entityName && get_class($entity) === $entityName) {
3571
                unset($this->entityInsertions[$hash]);
3572
            }
3573
        }
3574
    }
3575
3576
    /**
3577
     * @param ClassMetadata $class
3578
     * @param mixed         $identifierValue
3579
     *
3580
     * @return mixed the identifier after type conversion
3581
     *
3582
     * @throws \Doctrine\ORM\Mapping\MappingException if the entity has more than a single identifier
3583
     */
3584
    private function convertSingleFieldIdentifierToPHPValue(ClassMetadata $class, $identifierValue)
3585
    {
3586
        return $this->em->getConnection()->convertToPHPValue(
3587
            $identifierValue,
3588
            $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...
3589
        );
3590
    }
3591
}
3592