Completed
Pull Request — master (#5579)
by Huberty
05:57
created

UnitOfWork::isInIdentityMap()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 17
rs 9.4286
cc 3
eloc 9
nc 3
nop 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\Persistence\Mapping\RuntimeReflectionService;
23
use Doctrine\DBAL\LockMode;
24
use Doctrine\ORM\Internal\HydrationCompleteHandler;
25
use Doctrine\ORM\Mapping\Reflection\ReflectionPropertiesGetter;
26
use Exception;
27
use InvalidArgumentException;
28
use UnexpectedValueException;
29
30
use Doctrine\Common\Collections\ArrayCollection;
31
use Doctrine\Common\Collections\Collection;
32
use Doctrine\Common\NotifyPropertyChanged;
33
use Doctrine\Common\PropertyChangedListener;
34
use Doctrine\Common\Persistence\ObjectManagerAware;
35
use Doctrine\ORM\Mapping\ClassMetadata;
36
use Doctrine\ORM\Proxy\Proxy;
37
38
use Doctrine\ORM\Event\LifecycleEventArgs;
39
use Doctrine\ORM\Event\PreUpdateEventArgs;
40
use Doctrine\ORM\Event\PreFlushEventArgs;
41
use Doctrine\ORM\Event\OnFlushEventArgs;
42
use Doctrine\ORM\Event\PostFlushEventArgs;
43
use Doctrine\ORM\Event\ListenersInvoker;
44
45
use Doctrine\ORM\Cache\Persister\CachedPersister;
46
use Doctrine\ORM\Persisters\Entity\BasicEntityPersister;
47
use Doctrine\ORM\Persisters\Entity\SingleTablePersister;
48
use Doctrine\ORM\Persisters\Entity\JoinedSubclassPersister;
49
use Doctrine\ORM\Persisters\Collection\OneToManyPersister;
50
use Doctrine\ORM\Persisters\Collection\ManyToManyPersister;
51
use Doctrine\ORM\Utility\IdentifierFlattener;
52
use Doctrine\ORM\Cache\AssociationCacheEntry;
53
54
/**
55
 * The UnitOfWork is responsible for tracking changes to objects during an
56
 * "object-level" transaction and for writing out changes to the database
57
 * in the correct order.
58
 *
59
 * Internal note: This class contains highly performance-sensitive code.
60
 *
61
 * @since       2.0
62
 * @author      Benjamin Eberlei <[email protected]>
63
 * @author      Guilherme Blanco <[email protected]>
64
 * @author      Jonathan Wage <[email protected]>
65
 * @author      Roman Borschel <[email protected]>
66
 * @author      Rob Caiger <[email protected]>
67
 */
68
class UnitOfWork implements PropertyChangedListener
69
{
70
    /**
71
     * An entity is in MANAGED state when its persistence is managed by an EntityManager.
72
     */
73
    const STATE_MANAGED = 1;
74
75
    /**
76
     * An entity is new if it has just been instantiated (i.e. using the "new" operator)
77
     * and is not (yet) managed by an EntityManager.
78
     */
79
    const STATE_NEW = 2;
80
81
    /**
82
     * A detached entity is an instance with persistent state and identity that is not
83
     * (or no longer) associated with an EntityManager (and a UnitOfWork).
84
     */
85
    const STATE_DETACHED = 3;
86
87
    /**
88
     * A removed entity instance is an instance with a persistent identity,
89
     * associated with an EntityManager, whose persistent state will be deleted
90
     * on commit.
91
     */
92
    const STATE_REMOVED = 4;
93
94
    /**
95
     * Hint used to collect all primary keys of associated entities during hydration
96
     * and execute it in a dedicated query afterwards
97
     * @see https://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html?highlight=eager#temporarily-change-fetch-mode-in-dql
98
     */
99
    const HINT_DEFEREAGERLOAD = 'deferEagerLoad';
100
101
    /**
102
     * The identity map that holds references to all managed entities that have
103
     * an identity. The entities are grouped by their class name.
104
     * Since all classes in a hierarchy must share the same identifier set,
105
     * we always take the root class name of the hierarchy.
106
     *
107
     * @var array
108
     */
109
    private $identityMap = array();
110
111
    /**
112
     * Map of all identifiers of managed entities.
113
     * Keys are object ids (spl_object_hash).
114
     *
115
     * @var array
116
     */
117
    private $entityIdentifiers = array();
118
119
    /**
120
     * Map of the original entity data of managed entities.
121
     * Keys are object ids (spl_object_hash). This is used for calculating changesets
122
     * at commit time.
123
     *
124
     * Internal note: Note that PHPs "copy-on-write" behavior helps a lot with memory usage.
125
     *                A value will only really be copied if the value in the entity is modified
126
     *                by the user.
127
     *
128
     * @var array
129
     */
130
    private $originalEntityData = array();
131
132
    /**
133
     * Map of entity changes. Keys are object ids (spl_object_hash).
134
     * Filled at the beginning of a commit of the UnitOfWork and cleaned at the end.
135
     *
136
     * @var array
137
     */
138
    private $entityChangeSets = array();
139
140
    /**
141
     * The (cached) states of any known entities.
142
     * Keys are object ids (spl_object_hash).
143
     *
144
     * @var array
145
     */
146
    private $entityStates = array();
147
148
    /**
149
     * Map of entities that are scheduled for dirty checking at commit time.
150
     * This is only used for entities with a change tracking policy of DEFERRED_EXPLICIT.
151
     * Keys are object ids (spl_object_hash).
152
     *
153
     * @var array
154
     */
155
    private $scheduledForSynchronization = array();
156
157
    /**
158
     * A list of all pending entity insertions.
159
     *
160
     * @var array
161
     */
162
    private $entityInsertions = array();
163
164
    /**
165
     * A list of all pending entity updates.
166
     *
167
     * @var array
168
     */
169
    private $entityUpdates = array();
170
171
    /**
172
     * Any pending extra updates that have been scheduled by persisters.
173
     *
174
     * @var array
175
     */
176
    private $extraUpdates = array();
177
178
    /**
179
     * A list of all pending entity deletions.
180
     *
181
     * @var array
182
     */
183
    private $entityDeletions = array();
184
185
    /**
186
     * All pending collection deletions.
187
     *
188
     * @var array
189
     */
190
    private $collectionDeletions = array();
191
192
    /**
193
     * All pending collection updates.
194
     *
195
     * @var array
196
     */
197
    private $collectionUpdates = array();
198
199
    /**
200
     * List of collections visited during changeset calculation on a commit-phase of a UnitOfWork.
201
     * At the end of the UnitOfWork all these collections will make new snapshots
202
     * of their data.
203
     *
204
     * @var array
205
     */
206
    private $visitedCollections = array();
207
208
    /**
209
     * The EntityManager that "owns" this UnitOfWork instance.
210
     *
211
     * @var EntityManagerInterface
212
     */
213
    private $em;
214
215
    /**
216
     * The entity persister instances used to persist entity instances.
217
     *
218
     * @var array
219
     */
220
    private $persisters = array();
221
222
    /**
223
     * The collection persister instances used to persist collections.
224
     *
225
     * @var array
226
     */
227
    private $collectionPersisters = array();
228
229
    /**
230
     * The EventManager used for dispatching events.
231
     *
232
     * @var \Doctrine\Common\EventManager
233
     */
234
    private $evm;
235
236
    /**
237
     * The ListenersInvoker used for dispatching events.
238
     *
239
     * @var \Doctrine\ORM\Event\ListenersInvoker
240
     */
241
    private $listenersInvoker;
242
243
    /**
244
     * The IdentifierFlattener used for manipulating identifiers
245
     *
246
     * @var \Doctrine\ORM\Utility\IdentifierFlattener
247
     */
248
    private $identifierFlattener;
249
250
    /**
251
     * Orphaned entities that are scheduled for removal.
252
     *
253
     * @var array
254
     */
255
    private $orphanRemovals = array();
256
257
    /**
258
     * Read-Only objects are never evaluated
259
     *
260
     * @var array
261
     */
262
    private $readOnlyObjects = array();
263
264
    /**
265
     * Map of Entity Class-Names and corresponding IDs that should eager loaded when requested.
266
     *
267
     * @var array
268
     */
269
    private $eagerLoadingEntities = array();
270
271
    /**
272
     * @var boolean
273
     */
274
    protected $hasCache = false;
275
276
    /**
277
     * Helper for handling completion of hydration
278
     *
279
     * @var HydrationCompleteHandler
280
     */
281
    private $hydrationCompleteHandler;
282
283
    /**
284
     * @var ReflectionPropertiesGetter
285
     */
286
    private $reflectionPropertiesGetter;
287
288
    /**
289
     * Initializes a new UnitOfWork instance, bound to the given EntityManager.
290
     *
291
     * @param EntityManagerInterface $em
292
     */
293
    public function __construct(EntityManagerInterface $em)
294
    {
295
        $this->em                         = $em;
296
        $this->evm                        = $em->getEventManager();
297
        $this->listenersInvoker           = new ListenersInvoker($em);
298
        $this->hasCache                   = $em->getConfiguration()->isSecondLevelCacheEnabled();
299
        $this->identifierFlattener        = new IdentifierFlattener($this, $em->getMetadataFactory());
300
        $this->hydrationCompleteHandler   = new HydrationCompleteHandler($this->listenersInvoker, $em);
301
        $this->reflectionPropertiesGetter = new ReflectionPropertiesGetter(new RuntimeReflectionService());
302
    }
303
304
    /**
305
     * Commits the UnitOfWork, executing all operations that have been postponed
306
     * up to this point. The state of all managed entities will be synchronized with
307
     * the database.
308
     *
309
     * The operations are executed in the following order:
310
     *
311
     * 1) All entity insertions
312
     * 2) All entity updates
313
     * 3) All collection deletions
314
     * 4) All collection updates
315
     * 5) All entity deletions
316
     *
317
     * @param null|object|array $entity
318
     *
319
     * @return void
320
     *
321
     * @throws \Exception
322
     */
323
    public function commit($entity = null)
324
    {
325
        // Raise preFlush
326
        if ($this->evm->hasListeners(Events::preFlush)) {
327
            $this->evm->dispatchEvent(Events::preFlush, new PreFlushEventArgs($this->em));
328
        }
329
330
        // Compute changes done since last commit.
331
        if ($entity === null) {
332
            $this->computeChangeSets();
333
        } elseif (is_object($entity)) {
334
            $this->computeSingleEntityChangeSet($entity);
335
        } elseif (is_array($entity)) {
336
            foreach ($entity as $object) {
337
                $this->computeSingleEntityChangeSet($object);
338
            }
339
        }
340
341
        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...
342
                $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...
343
                $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...
344
                $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...
345
                $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...
346
                $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...
347
            $this->dispatchOnFlushEvent();
348
            $this->dispatchPostFlushEvent();
349
350
            return; // Nothing to do.
351
        }
352
353
        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...
354
            foreach ($this->orphanRemovals as $orphan) {
355
                $this->remove($orphan);
356
            }
357
        }
358
359
        $this->dispatchOnFlushEvent();
360
361
        // Now we need a commit order to maintain referential integrity
362
        $commitOrder = $this->getCommitOrder();
363
364
        $conn = $this->em->getConnection();
365
        $conn->beginTransaction();
366
367
        try {
368
            // Collection deletions (deletions of complete collections)
369
            foreach ($this->collectionDeletions as $collectionToDelete) {
370
                $this->getCollectionPersister($collectionToDelete->getMapping())->delete($collectionToDelete);
371
            }
372
373
            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...
374
                foreach ($commitOrder as $class) {
375
                    $this->executeInserts($class);
376
                }
377
            }
378
379
            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...
380
                foreach ($commitOrder as $class) {
381
                    $this->executeUpdates($class);
382
                }
383
            }
384
385
            // Extra updates that were requested by persisters.
386
            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...
387
                $this->executeExtraUpdates();
388
            }
389
390
            // Collection updates (deleteRows, updateRows, insertRows)
391
            foreach ($this->collectionUpdates as $collectionToUpdate) {
392
                $this->getCollectionPersister($collectionToUpdate->getMapping())->update($collectionToUpdate);
393
            }
394
395
            // Entity deletions come last and need to be in reverse commit order
396
            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...
397
                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...
398
                    $this->executeDeletions($commitOrder[$i]);
399
                }
400
            }
401
402
            $conn->commit();
403
        } catch (Exception $e) {
404
            $this->em->close();
405
            $conn->rollback();
406
407
            $this->afterTransactionRolledBack();
408
409
            throw $e;
410
        }
411
412
        $this->afterTransactionComplete();
413
414
        // Take new snapshots from visited collections
415
        foreach ($this->visitedCollections as $coll) {
416
            $coll->takeSnapshot();
417
        }
418
419
        $this->dispatchPostFlushEvent();
420
421
        $this->postCommitClear($entity);
422
    }
423
424
    /**
425
     * @param null|object|array $entity
426
     */
427
    private function postCommitClear($entity = null) {
428
429
        // Clear up
430
        $this->entityInsertions =
431
        $this->entityUpdates =
432
        $this->entityDeletions =
433
        $this->extraUpdates =
434
        $this->collectionUpdates =
435
        $this->collectionDeletions =
436
        $this->visitedCollections =
437
        $this->orphanRemovals = array();
438
439
        if ($entity === null) {
440
            $this->entityChangeSets = $this->scheduledForSynchronization = array();
441
        } elseif (is_object($entity)) {
442
            $oid = spl_object_hash($entity);
443
            $class = $this->em->getClassMetadata(get_class($entity));
444
            $this->clearEntityChangeSet($oid);
445
            $this->clearScheduledForSynchronization($class, $oid);
446
        } elseif (is_array($entity)) {
447
            foreach ($entity as $object) {
448
                $oid = spl_object_hash($object);
449
                $class = $this->em->getClassMetadata(get_class($object));
450
                $this->clearEntityChangeSet($oid);
451
                $this->clearScheduledForSynchronization($class, $oid);
452
            }
453
        }
454
    }
455
456
    /**
457
     * Computes the changesets of all entities scheduled for insertion.
458
     *
459
     * @return void
460
     */
461
    private function computeScheduleInsertsChangeSets()
462
    {
463
        foreach ($this->entityInsertions as $entity) {
464
            $class = $this->em->getClassMetadata(get_class($entity));
465
466
            $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...
467
        }
468
    }
469
470
    /**
471
     * Only flushes the given entity according to a ruleset that keeps the UoW consistent.
472
     *
473
     * 1. All entities scheduled for insertion, (orphan) removals and changes in collections are processed as well!
474
     * 2. Read Only entities are skipped.
475
     * 3. Proxies are skipped.
476
     * 4. Only if entity is properly managed.
477
     *
478
     * @param object $entity
479
     *
480
     * @return void
481
     *
482
     * @throws \InvalidArgumentException
483
     */
484
    private function computeSingleEntityChangeSet($entity)
485
    {
486
        $state = $this->getEntityState($entity);
487
488
        if ($state !== self::STATE_MANAGED && $state !== self::STATE_REMOVED) {
489
            throw new \InvalidArgumentException("Entity has to be managed or scheduled for removal for single computation " . self::objToStr($entity));
490
        }
491
492
        $class = $this->em->getClassMetadata(get_class($entity));
493
494
        if ($state === self::STATE_MANAGED && $class->isChangeTrackingDeferredImplicit()) {
495
            $this->persist($entity);
496
        }
497
498
        // Compute changes for INSERTed entities first. This must always happen even in this case.
499
        $this->computeScheduleInsertsChangeSets();
500
501
        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...
502
            return;
503
        }
504
505
        // Ignore uninitialized proxy objects
506
        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...
507
            return;
508
        }
509
510
        // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here.
511
        $oid = spl_object_hash($entity);
512
513 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...
514
            $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...
515
        }
516
    }
517
518
    /**
519
     * Executes any extra updates that have been scheduled.
520
     */
521
    private function executeExtraUpdates()
522
    {
523
        foreach ($this->extraUpdates as $oid => $update) {
524
            list ($entity, $changeset) = $update;
525
526
            $this->entityChangeSets[$oid] = $changeset;
527
            $this->getEntityPersister(get_class($entity))->update($entity);
528
        }
529
530
        $this->extraUpdates = array();
531
    }
532
533
    /**
534
     * Gets the changeset for an entity.
535
     *
536
     * @param object $entity
537
     *
538
     * @return array
539
     */
540
    public function getEntityChangeSet($entity)
541
    {
542
        $oid = spl_object_hash($entity);
543
544
        if (isset($this->entityChangeSets[$oid])) {
545
            return $this->entityChangeSets[$oid];
546
        }
547
548
        return array();
549
    }
550
551
    /**
552
     * Computes the changes that happened to a single entity.
553
     *
554
     * Modifies/populates the following properties:
555
     *
556
     * {@link _originalEntityData}
557
     * If the entity is NEW or MANAGED but not yet fully persisted (only has an id)
558
     * then it was not fetched from the database and therefore we have no original
559
     * entity data yet. All of the current entity data is stored as the original entity data.
560
     *
561
     * {@link _entityChangeSets}
562
     * The changes detected on all properties of the entity are stored there.
563
     * A change is a tuple array where the first entry is the old value and the second
564
     * entry is the new value of the property. Changesets are used by persisters
565
     * to INSERT/UPDATE the persistent entity state.
566
     *
567
     * {@link _entityUpdates}
568
     * If the entity is already fully MANAGED (has been fetched from the database before)
569
     * and any changes to its properties are detected, then a reference to the entity is stored
570
     * there to mark it for an update.
571
     *
572
     * {@link _collectionDeletions}
573
     * If a PersistentCollection has been de-referenced in a fully MANAGED entity,
574
     * then this collection is marked for deletion.
575
     *
576
     * @ignore
577
     *
578
     * @internal Don't call from the outside.
579
     *
580
     * @param ClassMetadata $class  The class descriptor of the entity.
581
     * @param object        $entity The entity for which to compute the changes.
582
     *
583
     * @return void
584
     */
585
    public function computeChangeSet(ClassMetadata $class, $entity)
586
    {
587
        $oid = spl_object_hash($entity);
588
589
        if (isset($this->readOnlyObjects[$oid])) {
590
            return;
591
        }
592
593
        if ( ! $class->isInheritanceTypeNone()) {
594
            $class = $this->em->getClassMetadata(get_class($entity));
595
        }
596
597
        $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...
598
599 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...
600
            $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...
601
        }
602
603
        $actualData = array();
604
605
        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...
606
            $value = $refProp->getValue($entity);
607
608
            if ($class->isCollectionValuedAssociation($name) && $value !== null) {
609
                if ($value instanceof PersistentCollection) {
610
                    if ($value->getOwner() === $entity) {
611
                        continue;
612
                    }
613
614
                    $value = new ArrayCollection($value->getValues());
615
                }
616
617
                // If $value is not a Collection then use an ArrayCollection.
618
                if ( ! $value instanceof Collection) {
619
                    $value = new ArrayCollection($value);
620
                }
621
622
                $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...
623
624
                // Inject PersistentCollection
625
                $value = new PersistentCollection(
626
                    $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...
627
                );
628
                $value->setOwner($entity, $assoc);
629
                $value->setDirty( ! $value->isEmpty());
630
631
                $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...
632
633
                $actualData[$name] = $value;
634
635
                continue;
636
            }
637
638
            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...
639
                $actualData[$name] = $value;
640
            }
641
        }
642
643
        if ( ! isset($this->originalEntityData[$oid])) {
644
            // Entity is either NEW or MANAGED but not yet fully persisted (only has an id).
645
            // These result in an INSERT.
646
            $this->originalEntityData[$oid] = $actualData;
647
            $changeSet = array();
648
649
            foreach ($actualData as $propName => $actualValue) {
650 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...
651
                    $changeSet[$propName] = array(null, $actualValue);
652
653
                    continue;
654
                }
655
656
                $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...
657
658
                if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
659
                    $changeSet[$propName] = array(null, $actualValue);
660
                }
661
            }
662
663
            $this->entityChangeSets[$oid] = $changeSet;
664
        } else {
665
            // Entity is "fully" MANAGED: it was already fully persisted before
666
            // and we have a copy of the original data
667
            $originalData           = $this->originalEntityData[$oid];
668
            $isChangeTrackingNotify = $class->isChangeTrackingNotify();
669
            $changeSet              = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid]))
670
                ? $this->entityChangeSets[$oid]
671
                : array();
672
673
            foreach ($actualData as $propName => $actualValue) {
674
                // skip field, its a partially omitted one!
675
                if ( ! (isset($originalData[$propName]) || array_key_exists($propName, $originalData))) {
676
                    continue;
677
                }
678
679
                $orgValue = $originalData[$propName];
680
681
                // skip if value haven't changed
682
                if ($orgValue === $actualValue) {
683
                    continue;
684
                }
685
686
                // if regular field
687 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...
688
                    if ($isChangeTrackingNotify) {
689
                        continue;
690
                    }
691
692
                    $changeSet[$propName] = array($orgValue, $actualValue);
693
694
                    continue;
695
                }
696
697
                $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...
698
699
                // Persistent collection was exchanged with the "originally"
700
                // created one. This can only mean it was cloned and replaced
701
                // on another entity.
702
                if ($actualValue instanceof PersistentCollection) {
703
                    $owner = $actualValue->getOwner();
704
                    if ($owner === null) { // cloned
705
                        $actualValue->setOwner($entity, $assoc);
706
                    } else if ($owner !== $entity) { // no clone, we have to fix
707
                        if (!$actualValue->isInitialized()) {
708
                            $actualValue->initialize(); // we have to do this otherwise the cols share state
709
                        }
710
                        $newValue = clone $actualValue;
711
                        $newValue->setOwner($entity, $assoc);
712
                        $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...
713
                    }
714
                }
715
716
                if ($orgValue instanceof PersistentCollection) {
717
                    // A PersistentCollection was de-referenced, so delete it.
718
                    $coid = spl_object_hash($orgValue);
719
720
                    if (isset($this->collectionDeletions[$coid])) {
721
                        continue;
722
                    }
723
724
                    $this->collectionDeletions[$coid] = $orgValue;
725
                    $changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored.
726
727
                    continue;
728
                }
729
730
                if ($assoc['type'] & ClassMetadata::TO_ONE) {
731
                    if ($assoc['isOwningSide']) {
732
                        $changeSet[$propName] = array($orgValue, $actualValue);
733
                    }
734
735
                    if ($orgValue !== null && $assoc['orphanRemoval']) {
736
                        $this->scheduleOrphanRemoval($orgValue);
737
                    }
738
                }
739
            }
740
741
            if ($changeSet) {
742
                $this->entityChangeSets[$oid]   = $changeSet;
743
                $this->originalEntityData[$oid] = $actualData;
744
                $this->entityUpdates[$oid]      = $entity;
745
            }
746
        }
747
748
        // Look for changes in associations of the entity
749
        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...
750
            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...
751
                continue;
752
            }
753
754
            $this->computeAssociationChanges($assoc, $val);
755
756
            if ( ! isset($this->entityChangeSets[$oid]) &&
757
                $assoc['isOwningSide'] &&
758
                $assoc['type'] == ClassMetadata::MANY_TO_MANY &&
759
                $val instanceof PersistentCollection &&
760
                $val->isDirty()) {
761
762
                $this->entityChangeSets[$oid]   = array();
763
                $this->originalEntityData[$oid] = $actualData;
764
                $this->entityUpdates[$oid]      = $entity;
765
            }
766
        }
767
    }
768
769
    /**
770
     * Computes all the changes that have been done to entities and collections
771
     * since the last commit and stores these changes in the _entityChangeSet map
772
     * temporarily for access by the persisters, until the UoW commit is finished.
773
     *
774
     * @return void
775
     */
776
    public function computeChangeSets()
777
    {
778
        // Compute changes for INSERTed entities first. This must always happen.
779
        $this->computeScheduleInsertsChangeSets();
780
781
        // Compute changes for other MANAGED entities. Change tracking policies take effect here.
782
        foreach ($this->identityMap as $className => $entities) {
783
            $class = $this->em->getClassMetadata($className);
784
785
            // Skip class if instances are read-only
786
            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...
787
                continue;
788
            }
789
790
            // If change tracking is explicit or happens through notification, then only compute
791
            // changes on entities of that type that are explicitly marked for synchronization.
792
            switch (true) {
793
                case ($class->isChangeTrackingDeferredImplicit()):
794
                    $entitiesToProcess = $entities;
795
                    break;
796
797
                case (isset($this->scheduledForSynchronization[$className])):
798
                    $entitiesToProcess = $this->scheduledForSynchronization[$className];
799
                    break;
800
801
                default:
802
                    $entitiesToProcess = array();
803
804
            }
805
806
            foreach ($entitiesToProcess as $entity) {
807
                // Ignore uninitialized proxy objects
808
                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...
809
                    continue;
810
                }
811
812
                // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here.
813
                $oid = spl_object_hash($entity);
814
815 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...
816
                    $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...
817
                }
818
            }
819
        }
820
    }
821
822
    /**
823
     * Computes the changes of an association.
824
     *
825
     * @param array $assoc The association mapping.
826
     * @param mixed $value The value of the association.
827
     *
828
     * @throws ORMInvalidArgumentException
829
     * @throws ORMException
830
     *
831
     * @return void
832
     */
833
    private function computeAssociationChanges($assoc, $value)
834
    {
835
        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...
836
            return;
837
        }
838
839
        if ($value instanceof PersistentCollection && $value->isDirty()) {
840
            $coid = spl_object_hash($value);
841
842
            $this->collectionUpdates[$coid] = $value;
843
            $this->visitedCollections[$coid] = $value;
844
        }
845
846
        // Look through the entities, and in any of their associations,
847
        // 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...
848
        // Unwrap. Uninitialized collections will simply be empty.
849
        $unwrappedValue = ($assoc['type'] & ClassMetadata::TO_ONE) ? array($value) : $value->unwrap();
850
        $targetClass    = $this->em->getClassMetadata($assoc['targetEntity']);
851
852
        foreach ($unwrappedValue as $key => $entry) {
853
            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...
854
                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...
855
            }
856
857
            $state = $this->getEntityState($entry, self::STATE_NEW);
858
859
            if ( ! ($entry instanceof $assoc['targetEntity'])) {
860
                throw ORMException::unexpectedAssociationValue($assoc['sourceEntity'], $assoc['fieldName'], get_class($entry), $assoc['targetEntity']);
861
            }
862
863
            switch ($state) {
864
                case self::STATE_NEW:
865
                    if ( ! $assoc['isCascadePersist']) {
866
                        throw ORMInvalidArgumentException::newEntityFoundThroughRelationship($assoc, $entry);
867
                    }
868
869
                    $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...
870
                    $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...
871
                    break;
872
873
                case self::STATE_REMOVED:
874
                    // Consume the $value as array (it's either an array or an ArrayAccess)
875
                    // and remove the element from Collection.
876
                    if ($assoc['type'] & ClassMetadata::TO_MANY) {
877
                        unset($value[$key]);
878
                    }
879
                    break;
880
881
                case self::STATE_DETACHED:
882
                    // Can actually not happen right now as we assume STATE_NEW,
883
                    // so the exception will be raised from the DBAL layer (constraint violation).
884
                    throw ORMInvalidArgumentException::detachedEntityFoundThroughRelationship($assoc, $entry);
885
                    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...
886
887
                default:
888
                    // MANAGED associated entities are already taken into account
889
                    // during changeset calculation anyway, since they are in the identity map.
890
            }
891
        }
892
    }
893
894
    /**
895
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
896
     * @param object                              $entity
897
     *
898
     * @return void
899
     */
900
    private function persistNew($class, $entity)
901
    {
902
        $oid    = spl_object_hash($entity);
903
        $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::prePersist);
904
905 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...
906
            $this->listenersInvoker->invoke($class, Events::prePersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
907
        }
908
909
        $idGen = $class->idGenerator;
910
911
        if ( ! $idGen->isPostInsertGenerator()) {
912
            $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...
913
914
            if ( ! $idGen instanceof \Doctrine\ORM\Id\AssignedGenerator) {
915
                $idValue = array($class->identifier[0] => $idValue);
916
917
                $class->setIdentifierValues($entity, $idValue);
918
            }
919
920
            $this->entityIdentifiers[$oid] = $idValue;
921
        }
922
923
        $this->entityStates[$oid] = self::STATE_MANAGED;
924
925
        $this->scheduleForInsert($entity);
926
    }
927
928
    /**
929
     * INTERNAL:
930
     * Computes the changeset of an individual entity, independently of the
931
     * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit().
932
     *
933
     * The passed entity must be a managed entity. If the entity already has a change set
934
     * because this method is invoked during a commit cycle then the change sets are added.
935
     * whereby changes detected in this method prevail.
936
     *
937
     * @ignore
938
     *
939
     * @param ClassMetadata $class  The class descriptor of the entity.
940
     * @param object        $entity The entity for which to (re)calculate the change set.
941
     *
942
     * @return void
943
     *
944
     * @throws ORMInvalidArgumentException If the passed entity is not MANAGED.
945
     */
946
    public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity)
947
    {
948
        $oid = spl_object_hash($entity);
949
950
        if ( ! isset($this->entityStates[$oid]) || $this->entityStates[$oid] != self::STATE_MANAGED) {
951
            throw ORMInvalidArgumentException::entityNotManaged($entity);
952
        }
953
954
        // skip if change tracking is "NOTIFY"
955
        if ($class->isChangeTrackingNotify()) {
956
            return;
957
        }
958
959
        if ( ! $class->isInheritanceTypeNone()) {
960
            $class = $this->em->getClassMetadata(get_class($entity));
961
        }
962
963
        $actualData = array();
964
965
        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...
966
            if (( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity())
967
                && ($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...
968
                && ! $class->isCollectionValuedAssociation($name)) {
969
                $actualData[$name] = $refProp->getValue($entity);
970
            }
971
        }
972
973
        if ( ! isset($this->originalEntityData[$oid])) {
974
            throw new \RuntimeException('Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.');
975
        }
976
977
        $originalData = $this->originalEntityData[$oid];
978
        $changeSet = array();
979
980
        foreach ($actualData as $propName => $actualValue) {
981
            $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
982
983
            if ($orgValue !== $actualValue) {
984
                $changeSet[$propName] = array($orgValue, $actualValue);
985
            }
986
        }
987
988
        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...
989
            if (isset($this->entityChangeSets[$oid])) {
990
                $this->entityChangeSets[$oid] = array_merge($this->entityChangeSets[$oid], $changeSet);
991
            } else if ( ! isset($this->entityInsertions[$oid])) {
992
                $this->entityChangeSets[$oid] = $changeSet;
993
                $this->entityUpdates[$oid]    = $entity;
994
            }
995
            $this->originalEntityData[$oid] = $actualData;
996
        }
997
    }
998
999
    /**
1000
     * Executes all entity insertions for entities of the specified type.
1001
     *
1002
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
1003
     *
1004
     * @return void
1005
     */
1006
    private function executeInserts($class)
1007
    {
1008
        $entities   = array();
1009
        $className  = $class->name;
1010
        $persister  = $this->getEntityPersister($className);
1011
        $invoke     = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist);
1012
1013
        foreach ($this->entityInsertions as $oid => $entity) {
1014
1015
            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...
1016
                continue;
1017
            }
1018
1019
            $persister->addInsert($entity);
1020
1021
            unset($this->entityInsertions[$oid]);
1022
1023
            if ($invoke !== ListenersInvoker::INVOKE_NONE) {
1024
                $entities[] = $entity;
1025
            }
1026
        }
1027
1028
        $postInsertIds = $persister->executeInserts();
1029
1030
        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...
1031
            // Persister returned post-insert IDs
1032
            foreach ($postInsertIds as $postInsertId) {
1033
                $id      = $postInsertId['generatedId'];
1034
                $entity  = $postInsertId['entity'];
1035
                $oid     = spl_object_hash($entity);
1036
                $idField = $class->identifier[0];
1037
1038
                $class->reflFields[$idField]->setValue($entity, $id);
1039
1040
                $this->entityIdentifiers[$oid] = array($idField => $id);
1041
                $this->entityStates[$oid] = self::STATE_MANAGED;
1042
                $this->originalEntityData[$oid][$idField] = $id;
1043
1044
                $this->addToIdentityMap($entity);
1045
            }
1046
        }
1047
1048
        foreach ($entities as $entity) {
1049
            $this->listenersInvoker->invoke($class, Events::postPersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
1050
        }
1051
    }
1052
1053
    /**
1054
     * Executes all entity updates for entities of the specified type.
1055
     *
1056
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
1057
     *
1058
     * @return void
1059
     */
1060
    private function executeUpdates($class)
1061
    {
1062
        $className          = $class->name;
1063
        $persister          = $this->getEntityPersister($className);
1064
        $preUpdateInvoke    = $this->listenersInvoker->getSubscribedSystems($class, Events::preUpdate);
1065
        $postUpdateInvoke   = $this->listenersInvoker->getSubscribedSystems($class, Events::postUpdate);
1066
1067
        foreach ($this->entityUpdates as $oid => $entity) {
1068
1069
            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...
1070
                continue;
1071
            }
1072
1073
            if ($preUpdateInvoke != ListenersInvoker::INVOKE_NONE) {
1074
                $this->listenersInvoker->invoke($class, Events::preUpdate, $entity, new PreUpdateEventArgs($entity, $this->em, $this->entityChangeSets[$oid]), $preUpdateInvoke);
1075
                $this->recomputeSingleEntityChangeSet($class, $entity);
1076
            }
1077
1078
            if ( ! empty($this->entityChangeSets[$oid])) {
1079
                $persister->update($entity);
1080
            }
1081
1082
            unset($this->entityUpdates[$oid]);
1083
1084 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...
1085
                $this->listenersInvoker->invoke($class, Events::postUpdate, $entity, new LifecycleEventArgs($entity, $this->em), $postUpdateInvoke);
1086
            }
1087
        }
1088
    }
1089
1090
    /**
1091
     * Executes all entity deletions for entities of the specified type.
1092
     *
1093
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
1094
     *
1095
     * @return void
1096
     */
1097
    private function executeDeletions($class)
1098
    {
1099
        $className  = $class->name;
1100
        $persister  = $this->getEntityPersister($className);
1101
        $invoke     = $this->listenersInvoker->getSubscribedSystems($class, Events::postRemove);
1102
1103
        foreach ($this->entityDeletions as $oid => $entity) {
1104
            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...
1105
                continue;
1106
            }
1107
1108
            $persister->delete($entity);
1109
1110
            unset(
1111
                $this->entityDeletions[$oid],
1112
                $this->entityIdentifiers[$oid],
1113
                $this->originalEntityData[$oid],
1114
                $this->entityStates[$oid]
1115
            );
1116
1117
            // Entity with this $oid after deletion treated as NEW, even if the $oid
1118
            // is obtained by a new entity because the old one went out of scope.
1119
            //$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...
1120
            if ( ! $class->isIdentifierNatural()) {
1121
                $class->reflFields[$class->identifier[0]]->setValue($entity, null);
1122
            }
1123
1124 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...
1125
                $this->listenersInvoker->invoke($class, Events::postRemove, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
1126
            }
1127
        }
1128
    }
1129
1130
    /**
1131
     * Gets the commit order.
1132
     *
1133
     * @param array|null $entityChangeSet
1134
     *
1135
     * @return array
1136
     */
1137
    private function getCommitOrder(array $entityChangeSet = null)
1138
    {
1139
        if ($entityChangeSet === null) {
1140
            $entityChangeSet = array_merge($this->entityInsertions, $this->entityUpdates, $this->entityDeletions);
1141
        }
1142
1143
        $calc = $this->getCommitOrderCalculator();
1144
1145
        // See if there are any new classes in the changeset, that are not in the
1146
        // commit order graph yet (don't have a node).
1147
        // We have to inspect changeSet to be able to correctly build dependencies.
1148
        // It is not possible to use IdentityMap here because post inserted ids
1149
        // are not yet available.
1150
        $newNodes = array();
1151
1152
        foreach ($entityChangeSet as $entity) {
1153
            $class = $this->em->getClassMetadata(get_class($entity));
1154
1155
            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...
1156
                continue;
1157
            }
1158
1159
            $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...
1160
1161
            $newNodes[] = $class;
1162
        }
1163
1164
        // Calculate dependencies for new nodes
1165
        while ($class = array_pop($newNodes)) {
1166
            foreach ($class->associationMappings as $assoc) {
1167
                if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) {
1168
                    continue;
1169
                }
1170
1171
                $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
1172
1173
                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...
1174
                    $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...
1175
1176
                    $newNodes[] = $targetClass;
1177
                }
1178
1179
                $joinColumns = reset($assoc['joinColumns']);
1180
                $isNullable  = isset($joinColumns['nullable']) ? $joinColumns['nullable'] : false;
1181
1182
                $calc->addDependency($targetClass->name, $class->name, $isNullable ? 0 : 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...
1183
1184
                // If the target class has mapped subclasses, these share the same dependency.
1185
                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...
1186
                    continue;
1187
                }
1188
1189
                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...
1190
                    $targetSubClass = $this->em->getClassMetadata($subClassName);
1191
1192
                    if ( ! $calc->hasNode($subClassName)) {
1193
                        $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...
1194
1195
                        $newNodes[] = $targetSubClass;
1196
                    }
1197
1198
                    $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...
1199
                }
1200
            }
1201
        }
1202
1203
        return $calc->sort();
1204
    }
1205
1206
    /**
1207
     * Schedules an entity for insertion into the database.
1208
     * If the entity already has an identifier, it will be added to the identity map.
1209
     *
1210
     * @param object $entity The entity to schedule for insertion.
1211
     *
1212
     * @return void
1213
     *
1214
     * @throws ORMInvalidArgumentException
1215
     * @throws \InvalidArgumentException
1216
     */
1217
    public function scheduleForInsert($entity)
1218
    {
1219
        $oid = spl_object_hash($entity);
1220
1221
        if (isset($this->entityUpdates[$oid])) {
1222
            throw new InvalidArgumentException("Dirty entity can not be scheduled for insertion.");
1223
        }
1224
1225
        if (isset($this->entityDeletions[$oid])) {
1226
            throw ORMInvalidArgumentException::scheduleInsertForRemovedEntity($entity);
1227
        }
1228
        if (isset($this->originalEntityData[$oid]) && ! isset($this->entityInsertions[$oid])) {
1229
            throw ORMInvalidArgumentException::scheduleInsertForManagedEntity($entity);
1230
        }
1231
1232
        if (isset($this->entityInsertions[$oid])) {
1233
            throw ORMInvalidArgumentException::scheduleInsertTwice($entity);
1234
        }
1235
1236
        $this->entityInsertions[$oid] = $entity;
1237
1238
        if (isset($this->entityIdentifiers[$oid])) {
1239
            $this->addToIdentityMap($entity);
1240
        }
1241
1242
        if ($entity instanceof NotifyPropertyChanged) {
1243
            $entity->addPropertyChangedListener($this);
1244
        }
1245
    }
1246
1247
    /**
1248
     * Checks whether an entity is scheduled for insertion.
1249
     *
1250
     * @param object $entity
1251
     *
1252
     * @return boolean
1253
     */
1254
    public function isScheduledForInsert($entity)
1255
    {
1256
        return isset($this->entityInsertions[spl_object_hash($entity)]);
1257
    }
1258
1259
    /**
1260
     * Schedules an entity for being updated.
1261
     *
1262
     * @param object $entity The entity to schedule for being updated.
1263
     *
1264
     * @return void
1265
     *
1266
     * @throws ORMInvalidArgumentException
1267
     */
1268
    public function scheduleForUpdate($entity)
1269
    {
1270
        $oid = spl_object_hash($entity);
1271
1272
        if ( ! isset($this->entityIdentifiers[$oid])) {
1273
            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "scheduling for update");
1274
        }
1275
1276
        if (isset($this->entityDeletions[$oid])) {
1277
            throw ORMInvalidArgumentException::entityIsRemoved($entity, "schedule for update");
1278
        }
1279
1280
        if ( ! isset($this->entityUpdates[$oid]) && ! isset($this->entityInsertions[$oid])) {
1281
            $this->entityUpdates[$oid] = $entity;
1282
        }
1283
    }
1284
1285
    /**
1286
     * INTERNAL:
1287
     * Schedules an extra update that will be executed immediately after the
1288
     * regular entity updates within the currently running commit cycle.
1289
     *
1290
     * Extra updates for entities are stored as (entity, changeset) tuples.
1291
     *
1292
     * @ignore
1293
     *
1294
     * @param object $entity    The entity for which to schedule an extra update.
1295
     * @param array  $changeset The changeset of the entity (what to update).
1296
     *
1297
     * @return void
1298
     */
1299
    public function scheduleExtraUpdate($entity, array $changeset)
1300
    {
1301
        $oid         = spl_object_hash($entity);
1302
        $extraUpdate = array($entity, $changeset);
1303
1304
        if (isset($this->extraUpdates[$oid])) {
1305
            list($ignored, $changeset2) = $this->extraUpdates[$oid];
0 ignored issues
show
Unused Code introduced by
The assignment to $ignored is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1306
1307
            $extraUpdate = array($entity, $changeset + $changeset2);
1308
        }
1309
1310
        $this->extraUpdates[$oid] = $extraUpdate;
1311
    }
1312
1313
    /**
1314
     * Checks whether an entity is registered as dirty in the unit of work.
1315
     * Note: Is not very useful currently as dirty entities are only registered
1316
     * at commit time.
1317
     *
1318
     * @param object $entity
1319
     *
1320
     * @return boolean
1321
     */
1322
    public function isScheduledForUpdate($entity)
1323
    {
1324
        return isset($this->entityUpdates[spl_object_hash($entity)]);
1325
    }
1326
1327
    /**
1328
     * Checks whether an entity is registered to be checked in the unit of work.
1329
     *
1330
     * @param object $entity
1331
     *
1332
     * @return boolean
1333
     */
1334
    public function isScheduledForDirtyCheck($entity)
1335
    {
1336
        $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...
1337
1338
        return isset($this->scheduledForSynchronization[$rootEntityName][spl_object_hash($entity)]);
1339
    }
1340
1341
    /**
1342
     * INTERNAL:
1343
     * Schedules an entity for deletion.
1344
     *
1345
     * @param object $entity
1346
     *
1347
     * @return void
1348
     */
1349
    public function scheduleForDelete($entity)
1350
    {
1351
        $oid = spl_object_hash($entity);
1352
1353
        if (isset($this->entityInsertions[$oid])) {
1354
            if ($this->isInIdentityMap($entity)) {
1355
                $this->removeFromIdentityMap($entity);
1356
            }
1357
1358
            unset($this->entityInsertions[$oid], $this->entityStates[$oid]);
1359
1360
            return; // entity has not been persisted yet, so nothing more to do.
1361
        }
1362
1363
        if ( ! $this->isInIdentityMap($entity)) {
1364
            return;
1365
        }
1366
1367
        $this->removeFromIdentityMap($entity);
1368
1369
        if (isset($this->entityUpdates[$oid])) {
1370
            unset($this->entityUpdates[$oid]);
1371
        }
1372
1373
        if ( ! isset($this->entityDeletions[$oid])) {
1374
            $this->entityDeletions[$oid] = $entity;
1375
            $this->entityStates[$oid]    = self::STATE_REMOVED;
1376
        }
1377
    }
1378
1379
    /**
1380
     * Checks whether an entity is registered as removed/deleted with the unit
1381
     * of work.
1382
     *
1383
     * @param object $entity
1384
     *
1385
     * @return boolean
1386
     */
1387
    public function isScheduledForDelete($entity)
1388
    {
1389
        return isset($this->entityDeletions[spl_object_hash($entity)]);
1390
    }
1391
1392
    /**
1393
     * Checks whether an entity is scheduled for insertion, update or deletion.
1394
     *
1395
     * @param object $entity
1396
     *
1397
     * @return boolean
1398
     */
1399
    public function isEntityScheduled($entity)
1400
    {
1401
        $oid = spl_object_hash($entity);
1402
1403
        return isset($this->entityInsertions[$oid])
1404
            || isset($this->entityUpdates[$oid])
1405
            || isset($this->entityDeletions[$oid]);
1406
    }
1407
1408
    /**
1409
     * INTERNAL:
1410
     * Registers an entity in the identity map.
1411
     * Note that entities in a hierarchy are registered with the class name of
1412
     * the root entity.
1413
     *
1414
     * @ignore
1415
     *
1416
     * @param object $entity The entity to register.
1417
     *
1418
     * @return boolean TRUE if the registration was successful, FALSE if the identity of
1419
     *                 the entity in question is already managed.
1420
     *
1421
     * @throws ORMInvalidArgumentException
1422
     */
1423 View Code Duplication
    public function addToIdentityMap($entity)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
1424
    {
1425
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1426
        $idHash        = implode(' ', $this->entityIdentifiers[spl_object_hash($entity)]);
1427
1428
        if ($idHash === '') {
1429
            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...
1430
        }
1431
1432
        $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...
1433
1434
        if (isset($this->identityMap[$className][$idHash])) {
1435
            return false;
1436
        }
1437
1438
        $this->identityMap[$className][$idHash] = $entity;
1439
1440
        return true;
1441
    }
1442
1443
    /**
1444
     * Gets the state of an entity with regard to the current unit of work.
1445
     *
1446
     * @param object   $entity
1447
     * @param int|null $assume The state to assume if the state is not yet known (not MANAGED or REMOVED).
1448
     *                         This parameter can be set to improve performance of entity state detection
1449
     *                         by potentially avoiding a database lookup if the distinction between NEW and DETACHED
1450
     *                         is either known or does not matter for the caller of the method.
1451
     *
1452
     * @return int The entity state.
1453
     */
1454
    public function getEntityState($entity, $assume = null)
1455
    {
1456
        $oid = spl_object_hash($entity);
1457
1458
        if (isset($this->entityStates[$oid])) {
1459
            return $this->entityStates[$oid];
1460
        }
1461
1462
        if ($assume !== null) {
1463
            return $assume;
1464
        }
1465
1466
        // State can only be NEW or DETACHED, because MANAGED/REMOVED states are known.
1467
        // Note that you can not remember the NEW or DETACHED state in _entityStates since
1468
        // the UoW does not hold references to such objects and the object hash can be reused.
1469
        // More generally because the state may "change" between NEW/DETACHED without the UoW being aware of it.
1470
        $class = $this->em->getClassMetadata(get_class($entity));
1471
        $id    = $class->getIdentifierValues($entity);
1472
1473
        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...
1474
            return self::STATE_NEW;
1475
        }
1476
1477
        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...
1478
            $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...
1479
        }
1480
1481
        switch (true) {
1482
            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...
1483
                // Check for a version field, if available, to avoid a db lookup.
1484
                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...
1485
                    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...
1486
                        ? self::STATE_DETACHED
1487
                        : self::STATE_NEW;
1488
                }
1489
1490
                // Last try before db lookup: check the identity map.
1491
                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...
1492
                    return self::STATE_DETACHED;
1493
                }
1494
1495
                // db lookup
1496
                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...
1497
                    return self::STATE_DETACHED;
1498
                }
1499
1500
                return self::STATE_NEW;
1501
1502
            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...
1503
                // if we have a pre insert generator we can't be sure that having an id
1504
                // really means that the entity exists. We have to verify this through
1505
                // the last resort: a db lookup
1506
1507
                // Last try before db lookup: check the identity map.
1508
                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...
1509
                    return self::STATE_DETACHED;
1510
                }
1511
1512
                // db lookup
1513
                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...
1514
                    return self::STATE_DETACHED;
1515
                }
1516
1517
                return self::STATE_NEW;
1518
1519
            default:
1520
                return self::STATE_DETACHED;
1521
        }
1522
    }
1523
1524
    /**
1525
     * INTERNAL:
1526
     * Removes an entity from the identity map. This effectively detaches the
1527
     * entity from the persistence management of Doctrine.
1528
     *
1529
     * @ignore
1530
     *
1531
     * @param object $entity
1532
     *
1533
     * @return boolean
1534
     *
1535
     * @throws ORMInvalidArgumentException
1536
     */
1537 View Code Duplication
    public function removeFromIdentityMap($entity)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
1538
    {
1539
        $oid           = spl_object_hash($entity);
1540
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1541
        $idHash        = implode(' ', $this->entityIdentifiers[$oid]);
1542
1543
        if ($idHash === '') {
1544
            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "remove from identity map");
1545
        }
1546
1547
        $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...
1548
1549
        if (isset($this->identityMap[$className][$idHash])) {
1550
            unset($this->identityMap[$className][$idHash]);
1551
            unset($this->readOnlyObjects[$oid]);
1552
1553
            //$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...
1554
1555
            return true;
1556
        }
1557
1558
        return false;
1559
    }
1560
1561
    /**
1562
     * INTERNAL:
1563
     * Gets an entity in the identity map by its identifier hash.
1564
     *
1565
     * @ignore
1566
     *
1567
     * @param string $idHash
1568
     * @param string $rootClassName
1569
     *
1570
     * @return object
1571
     */
1572
    public function getByIdHash($idHash, $rootClassName)
1573
    {
1574
        return $this->identityMap[$rootClassName][$idHash];
1575
    }
1576
1577
    /**
1578
     * INTERNAL:
1579
     * Tries to get an entity by its identifier hash. If no entity is found for
1580
     * the given hash, FALSE is returned.
1581
     *
1582
     * @ignore
1583
     *
1584
     * @param mixed  $idHash        (must be possible to cast it to string)
1585
     * @param string $rootClassName
1586
     *
1587
     * @return object|bool The found entity or FALSE.
1588
     */
1589 View Code Duplication
    public function tryGetByIdHash($idHash, $rootClassName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
1590
    {
1591
        $stringIdHash = (string) $idHash;
1592
1593
        if (isset($this->identityMap[$rootClassName][$stringIdHash])) {
1594
            return $this->identityMap[$rootClassName][$stringIdHash];
1595
        }
1596
1597
        return false;
1598
    }
1599
1600
    /**
1601
     * Checks whether an entity is registered in the identity map of this UnitOfWork.
1602
     *
1603
     * @param object $entity
1604
     *
1605
     * @return boolean
1606
     */
1607
    public function isInIdentityMap($entity)
1608
    {
1609
        $oid = spl_object_hash($entity);
1610
1611
        if ( ! isset($this->entityIdentifiers[$oid])) {
1612
            return false;
1613
        }
1614
1615
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1616
        $idHash        = implode(' ', $this->entityIdentifiers[$oid]);
1617
1618
        if ($idHash === '') {
1619
            return false;
1620
        }
1621
1622
        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...
1623
    }
1624
1625
    /**
1626
     * INTERNAL:
1627
     * Checks whether an identifier hash exists in the identity map.
1628
     *
1629
     * @ignore
1630
     *
1631
     * @param string $idHash
1632
     * @param string $rootClassName
1633
     *
1634
     * @return boolean
1635
     */
1636
    public function containsIdHash($idHash, $rootClassName)
1637
    {
1638
        return isset($this->identityMap[$rootClassName][$idHash]);
1639
    }
1640
1641
    /**
1642
     * Persists an entity as part of the current unit of work.
1643
     *
1644
     * @param object $entity The entity to persist.
1645
     *
1646
     * @return void
1647
     */
1648
    public function persist($entity)
1649
    {
1650
        $visited = array();
1651
1652
        $this->doPersist($entity, $visited);
1653
    }
1654
1655
    /**
1656
     * Persists an entity as part of the current unit of work.
1657
     *
1658
     * This method is internally called during persist() cascades as it tracks
1659
     * the already visited entities to prevent infinite recursions.
1660
     *
1661
     * @param object $entity  The entity to persist.
1662
     * @param array  $visited The already visited entities.
1663
     *
1664
     * @return void
1665
     *
1666
     * @throws ORMInvalidArgumentException
1667
     * @throws UnexpectedValueException
1668
     */
1669
    private function doPersist($entity, array &$visited)
1670
    {
1671
        $oid = spl_object_hash($entity);
1672
1673
        if (isset($visited[$oid])) {
1674
            return; // Prevent infinite recursion
1675
        }
1676
1677
        $visited[$oid] = $entity; // Mark visited
1678
1679
        $class = $this->em->getClassMetadata(get_class($entity));
1680
1681
        // We assume NEW, so DETACHED entities result in an exception on flush (constraint violation).
1682
        // If we would detect DETACHED here we would throw an exception anyway with the same
1683
        // consequences (not recoverable/programming error), so just assuming NEW here
1684
        // lets us avoid some database lookups for entities with natural identifiers.
1685
        $entityState = $this->getEntityState($entity, self::STATE_NEW);
1686
1687
        switch ($entityState) {
1688
            case self::STATE_MANAGED:
1689
                // Nothing to do, except if policy is "deferred explicit"
1690
                if ($class->isChangeTrackingDeferredExplicit()) {
1691
                    $this->scheduleForDirtyCheck($entity);
1692
                }
1693
                break;
1694
1695
            case self::STATE_NEW:
1696
                $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...
1697
                break;
1698
1699
            case self::STATE_REMOVED:
1700
                // Entity becomes managed again
1701
                unset($this->entityDeletions[$oid]);
1702
                $this->addToIdentityMap($entity);
1703
1704
                $this->entityStates[$oid] = self::STATE_MANAGED;
1705
                break;
1706
1707
            case self::STATE_DETACHED:
1708
                // Can actually not happen right now since we assume STATE_NEW.
1709
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "persisted");
1710
1711
            default:
1712
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1713
        }
1714
1715
        $this->cascadePersist($entity, $visited);
1716
    }
1717
1718
    /**
1719
     * Deletes an entity as part of the current unit of work.
1720
     *
1721
     * @param object $entity The entity to remove.
1722
     *
1723
     * @return void
1724
     */
1725
    public function remove($entity)
1726
    {
1727
        $visited = array();
1728
1729
        $this->doRemove($entity, $visited);
1730
    }
1731
1732
    /**
1733
     * Deletes an entity as part of the current unit of work.
1734
     *
1735
     * This method is internally called during delete() cascades as it tracks
1736
     * the already visited entities to prevent infinite recursions.
1737
     *
1738
     * @param object $entity  The entity to delete.
1739
     * @param array  $visited The map of the already visited entities.
1740
     *
1741
     * @return void
1742
     *
1743
     * @throws ORMInvalidArgumentException If the instance is a detached entity.
1744
     * @throws UnexpectedValueException
1745
     */
1746
    private function doRemove($entity, array &$visited)
1747
    {
1748
        $oid = spl_object_hash($entity);
1749
1750
        if (isset($visited[$oid])) {
1751
            return; // Prevent infinite recursion
1752
        }
1753
1754
        $visited[$oid] = $entity; // mark visited
1755
1756
        // Cascade first, because scheduleForDelete() removes the entity from the identity map, which
1757
        // can cause problems when a lazy proxy has to be initialized for the cascade operation.
1758
        $this->cascadeRemove($entity, $visited);
1759
1760
        $class       = $this->em->getClassMetadata(get_class($entity));
1761
        $entityState = $this->getEntityState($entity);
1762
1763
        switch ($entityState) {
1764
            case self::STATE_NEW:
1765
            case self::STATE_REMOVED:
1766
                // nothing to do
1767
                break;
1768
1769
            case self::STATE_MANAGED:
1770
                $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...
1771
1772 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...
1773
                    $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...
1774
                }
1775
1776
                $this->scheduleForDelete($entity);
1777
                break;
1778
1779
            case self::STATE_DETACHED:
1780
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "removed");
1781
            default:
1782
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1783
        }
1784
1785
    }
1786
1787
    /**
1788
     * Merges the state of the given detached entity into this UnitOfWork.
1789
     *
1790
     * @param object $entity
1791
     *
1792
     * @return object The managed copy of the entity.
1793
     *
1794
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1795
     *         attribute and the version check against the managed copy fails.
1796
     *
1797
     * @todo Require active transaction!? OptimisticLockException may result in undefined state!?
1798
     */
1799
    public function merge($entity)
1800
    {
1801
        $visited = array();
1802
1803
        return $this->doMerge($entity, $visited);
1804
    }
1805
1806
    /**
1807
     * Executes a merge operation on an entity.
1808
     *
1809
     * @param object      $entity
1810
     * @param array       $visited
1811
     * @param object|null $prevManagedCopy
1812
     * @param array|null  $assoc
1813
     *
1814
     * @return object The managed copy of the entity.
1815
     *
1816
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1817
     *         attribute and the version check against the managed copy fails.
1818
     * @throws ORMInvalidArgumentException If the entity instance is NEW.
1819
     * @throws EntityNotFoundException
1820
     */
1821
    private function doMerge($entity, array &$visited, $prevManagedCopy = null, $assoc = null)
1822
    {
1823
        $oid = spl_object_hash($entity);
1824
1825
        if (isset($visited[$oid])) {
1826
            $managedCopy = $visited[$oid];
1827
1828
            if ($prevManagedCopy !== null) {
1829
                $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy);
0 ignored issues
show
Bug introduced by
It seems like $assoc defined by parameter $assoc on line 1821 can also be of type null; however, Doctrine\ORM\UnitOfWork:...ationWithMergedEntity() does only seem to accept array, 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...
1830
            }
1831
1832
            return $managedCopy;
1833
        }
1834
1835
        $class = $this->em->getClassMetadata(get_class($entity));
1836
1837
        // First we assume DETACHED, although it can still be NEW but we can avoid
1838
        // an extra db-roundtrip this way. If it is not MANAGED but has an identity,
1839
        // we need to fetch it from the db anyway in order to merge.
1840
        // MANAGED entities are ignored by the merge operation.
1841
        $managedCopy = $entity;
1842
1843
        if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) {
1844
            // Try to look the entity up in the identity map.
1845
            $id = $class->getIdentifierValues($entity);
1846
1847
            // If there is no ID, it is actually NEW.
1848
            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...
1849
                $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...
1850
1851
                $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...
1852
            } else {
1853
                $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...
1854
                    ? $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...
1855
                    : $id;
1856
1857
                $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 1921 which is incompatible with the return type documented by Doctrine\ORM\UnitOfWork::doMerge of type object.
Loading history...
1858
1859
                if ($managedCopy) {
1860
                    // We have the entity in-memory already, just make sure its not removed.
1861
                    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 1857 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...
1862
                        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 1857 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...
1863
                    }
1864
                } else {
1865
                    // We need to fetch the managed copy in order to merge.
1866
                    $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...
1867
                }
1868
1869
                if ($managedCopy === null) {
1870
                    // If the identifier is ASSIGNED, it is NEW, otherwise an error
1871
                    // since the managed entity was not found.
1872
                    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...
1873
                        throw EntityNotFoundException::fromClassNameAndIdentifier(
1874
                            $class->getName(),
1875
                            $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...
1876
                        );
1877
                    }
1878
1879
                    $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...
1880
                    $class->setIdentifierValues($managedCopy, $id);
1881
1882
                    $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...
1883
                }
1884
            }
1885
1886
            if ($class->isVersioned && $this->isLoaded($managedCopy) && $this->isLoaded($entity)) {
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...
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1857 can also be of type boolean; however, Doctrine\ORM\UnitOfWork::isLoaded() 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...
1887
                $reflField          = $class->reflFields[$class->versionField];
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...
1888
                $managedCopyVersion = $reflField->getValue($managedCopy);
1889
                $entityVersion      = $reflField->getValue($entity);
1890
1891
                // Throw exception if versions don't match.
1892
                if ($managedCopyVersion != $entityVersion) {
1893
                    throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion);
1894
                }
1895
            }
1896
1897
            $visited[$oid] = $managedCopy; // mark visited
1898
1899
            if ($this->isLoaded($entity)) {
1900
                if ($managedCopy instanceof Proxy && ! $managedCopy->__isInitialized()) {
1901
                    $managedCopy->__load();
1902
                }
1903
1904
                $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy);
0 ignored issues
show
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1857 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...
1905
            }
1906
1907
            if ($class->isChangeTrackingDeferredExplicit()) {
1908
                $this->scheduleForDirtyCheck($entity);
1909
            }
1910
        }
1911
1912
        if ($prevManagedCopy !== null) {
1913
            $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy);
0 ignored issues
show
Bug introduced by
It seems like $assoc defined by parameter $assoc on line 1821 can also be of type null; however, Doctrine\ORM\UnitOfWork:...ationWithMergedEntity() does only seem to accept array, 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...
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1857 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...
1914
        }
1915
1916
        // Mark the managed copy visited as well
1917
        $visited[spl_object_hash($managedCopy)] = $managedCopy;
1918
1919
        $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 1857 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...
1920
1921
        return $managedCopy;
1922
    }
1923
1924
    /**
1925
     * Tests if an entity is loaded - must either be a loaded proxy or not a proxy
1926
     *
1927
     * @param object $entity
1928
     *
1929
     * @return bool
1930
     */
1931
    private function isLoaded($entity)
1932
    {
1933
        return !($entity instanceof Proxy) || $entity->__isInitialized();
1934
    }
1935
1936
    /**
1937
     * Sets/adds associated managed copies into the previous entity's association field
1938
     *
1939
     * @param object $entity
1940
     * @param array  $association
1941
     * @param object $previousManagedCopy
1942
     * @param object $managedCopy
1943
     *
1944
     * @return void
1945
     */
1946
    private function updateAssociationWithMergedEntity($entity, array $association, $previousManagedCopy, $managedCopy)
1947
    {
1948
        $assocField = $association['fieldName'];
1949
        $prevClass  = $this->em->getClassMetadata(get_class($previousManagedCopy));
1950
1951
        if ($association['type'] & ClassMetadata::TO_ONE) {
1952
            $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...
1953
1954
            return;
1955
        }
1956
1957
        $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...
1958
        $value[] = $managedCopy;
1959
1960 View Code Duplication
        if ($association['type'] == ClassMetadata::ONE_TO_MANY) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1961
            $class = $this->em->getClassMetadata(get_class($entity));
1962
1963
            $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...
1964
        }
1965
    }
1966
1967
    /**
1968
     * Detaches an entity from the persistence management. It's persistence will
1969
     * no longer be managed by Doctrine.
1970
     *
1971
     * @param object $entity The entity to detach.
1972
     *
1973
     * @return void
1974
     */
1975
    public function detach($entity)
1976
    {
1977
        $visited = array();
1978
1979
        $this->doDetach($entity, $visited);
1980
    }
1981
1982
    /**
1983
     * Executes a detach operation on the given entity.
1984
     *
1985
     * @param object  $entity
1986
     * @param array   $visited
1987
     * @param boolean $noCascade if true, don't cascade detach operation.
1988
     *
1989
     * @return void
1990
     */
1991
    private function doDetach($entity, array &$visited, $noCascade = false)
1992
    {
1993
        $oid = spl_object_hash($entity);
1994
1995
        if (isset($visited[$oid])) {
1996
            return; // Prevent infinite recursion
1997
        }
1998
1999
        $visited[$oid] = $entity; // mark visited
2000
2001
        switch ($this->getEntityState($entity, self::STATE_DETACHED)) {
2002
            case self::STATE_MANAGED:
2003
                if ($this->isInIdentityMap($entity)) {
2004
                    $this->removeFromIdentityMap($entity);
2005
                }
2006
2007
                unset(
2008
                    $this->entityInsertions[$oid],
2009
                    $this->entityUpdates[$oid],
2010
                    $this->entityDeletions[$oid],
2011
                    $this->entityIdentifiers[$oid],
2012
                    $this->entityStates[$oid],
2013
                    $this->originalEntityData[$oid]
2014
                );
2015
                break;
2016
            case self::STATE_NEW:
2017
            case self::STATE_DETACHED:
2018
                return;
2019
        }
2020
2021
        if ( ! $noCascade) {
2022
            $this->cascadeDetach($entity, $visited);
2023
        }
2024
    }
2025
2026
    /**
2027
     * Refreshes the state of the given entity from the database, overwriting
2028
     * any local, unpersisted changes.
2029
     *
2030
     * @param object $entity The entity to refresh.
2031
     *
2032
     * @return void
2033
     *
2034
     * @throws InvalidArgumentException If the entity is not MANAGED.
2035
     */
2036
    public function refresh($entity)
2037
    {
2038
        $visited = array();
2039
2040
        $this->doRefresh($entity, $visited);
2041
    }
2042
2043
    /**
2044
     * Executes a refresh operation on an entity.
2045
     *
2046
     * @param object $entity  The entity to refresh.
2047
     * @param array  $visited The already visited entities during cascades.
2048
     *
2049
     * @return void
2050
     *
2051
     * @throws ORMInvalidArgumentException If the entity is not MANAGED.
2052
     */
2053
    private function doRefresh($entity, array &$visited)
2054
    {
2055
        $oid = spl_object_hash($entity);
2056
2057
        if (isset($visited[$oid])) {
2058
            return; // Prevent infinite recursion
2059
        }
2060
2061
        $visited[$oid] = $entity; // mark visited
2062
2063
        $class = $this->em->getClassMetadata(get_class($entity));
2064
2065
        if ($this->getEntityState($entity) !== self::STATE_MANAGED) {
2066
            throw ORMInvalidArgumentException::entityNotManaged($entity);
2067
        }
2068
2069
        $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...
2070
            array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
2071
            $entity
2072
        );
2073
2074
        $this->cascadeRefresh($entity, $visited);
2075
    }
2076
2077
    /**
2078
     * Cascades a refresh operation to associated entities.
2079
     *
2080
     * @param object $entity
2081
     * @param array  $visited
2082
     *
2083
     * @return void
2084
     */
2085 View Code Duplication
    private function cascadeRefresh($entity, array &$visited)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
2086
    {
2087
        $class = $this->em->getClassMetadata(get_class($entity));
2088
2089
        $associationMappings = array_filter(
2090
            $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...
2091
            function ($assoc) { return $assoc['isCascadeRefresh']; }
2092
        );
2093
2094
        foreach ($associationMappings as $assoc) {
2095
            $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...
2096
2097
            switch (true) {
2098
                case ($relatedEntities instanceof PersistentCollection):
2099
                    // Unwrap so that foreach() does not initialize
2100
                    $relatedEntities = $relatedEntities->unwrap();
2101
                    // break; is commented intentionally!
2102
2103
                case ($relatedEntities instanceof Collection):
2104
                case (is_array($relatedEntities)):
2105
                    foreach ($relatedEntities as $relatedEntity) {
2106
                        $this->doRefresh($relatedEntity, $visited);
2107
                    }
2108
                    break;
2109
2110
                case ($relatedEntities !== null):
2111
                    $this->doRefresh($relatedEntities, $visited);
2112
                    break;
2113
2114
                default:
2115
                    // Do nothing
2116
            }
2117
        }
2118
    }
2119
2120
    /**
2121
     * Cascades a detach operation to associated entities.
2122
     *
2123
     * @param object $entity
2124
     * @param array  $visited
2125
     *
2126
     * @return void
2127
     */
2128 View Code Duplication
    private function cascadeDetach($entity, array &$visited)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
2129
    {
2130
        $class = $this->em->getClassMetadata(get_class($entity));
2131
2132
        $associationMappings = array_filter(
2133
            $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...
2134
            function ($assoc) { return $assoc['isCascadeDetach']; }
2135
        );
2136
2137
        foreach ($associationMappings as $assoc) {
2138
            $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...
2139
2140
            switch (true) {
2141
                case ($relatedEntities instanceof PersistentCollection):
2142
                    // Unwrap so that foreach() does not initialize
2143
                    $relatedEntities = $relatedEntities->unwrap();
2144
                    // break; is commented intentionally!
2145
2146
                case ($relatedEntities instanceof Collection):
2147
                case (is_array($relatedEntities)):
2148
                    foreach ($relatedEntities as $relatedEntity) {
2149
                        $this->doDetach($relatedEntity, $visited);
2150
                    }
2151
                    break;
2152
2153
                case ($relatedEntities !== null):
2154
                    $this->doDetach($relatedEntities, $visited);
2155
                    break;
2156
2157
                default:
2158
                    // Do nothing
2159
            }
2160
        }
2161
    }
2162
2163
    /**
2164
     * Cascades a merge operation to associated entities.
2165
     *
2166
     * @param object $entity
2167
     * @param object $managedCopy
2168
     * @param array  $visited
2169
     *
2170
     * @return void
2171
     */
2172
    private function cascadeMerge($entity, $managedCopy, array &$visited)
2173
    {
2174
        $class = $this->em->getClassMetadata(get_class($entity));
2175
2176
        $associationMappings = array_filter(
2177
            $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...
2178
            function ($assoc) { return $assoc['isCascadeMerge']; }
2179
        );
2180
2181
        foreach ($associationMappings as $assoc) {
2182
            $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...
2183
2184
            if ($relatedEntities instanceof Collection) {
2185
                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...
2186
                    continue;
2187
                }
2188
2189
                if ($relatedEntities instanceof PersistentCollection) {
2190
                    // Unwrap so that foreach() does not initialize
2191
                    $relatedEntities = $relatedEntities->unwrap();
2192
                }
2193
2194
                foreach ($relatedEntities as $relatedEntity) {
2195
                    $this->doMerge($relatedEntity, $visited, $managedCopy, $assoc);
2196
                }
2197
            } else if ($relatedEntities !== null) {
2198
                $this->doMerge($relatedEntities, $visited, $managedCopy, $assoc);
2199
            }
2200
        }
2201
    }
2202
2203
    /**
2204
     * Cascades the save operation to associated entities.
2205
     *
2206
     * @param object $entity
2207
     * @param array  $visited
2208
     *
2209
     * @return void
2210
     */
2211
    private function cascadePersist($entity, array &$visited)
2212
    {
2213
        $class = $this->em->getClassMetadata(get_class($entity));
2214
2215
        $associationMappings = array_filter(
2216
            $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...
2217
            function ($assoc) { return $assoc['isCascadePersist']; }
2218
        );
2219
2220
        foreach ($associationMappings as $assoc) {
2221
            $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...
2222
2223
            switch (true) {
2224
                case ($relatedEntities instanceof PersistentCollection):
2225
                    // Unwrap so that foreach() does not initialize
2226
                    $relatedEntities = $relatedEntities->unwrap();
2227
                    // break; is commented intentionally!
2228
2229
                case ($relatedEntities instanceof Collection):
2230
                case (is_array($relatedEntities)):
2231
                    if (($assoc['type'] & ClassMetadata::TO_MANY) <= 0) {
2232
                        throw ORMInvalidArgumentException::invalidAssociation(
2233
                            $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...
2234
                            $assoc,
2235
                            $relatedEntities
2236
                        );
2237
                    }
2238
2239
                    foreach ($relatedEntities as $relatedEntity) {
2240
                        $this->doPersist($relatedEntity, $visited);
2241
                    }
2242
2243
                    break;
2244
2245
                case ($relatedEntities !== null):
2246
                    if (! $relatedEntities instanceof $assoc['targetEntity']) {
2247
                        throw ORMInvalidArgumentException::invalidAssociation(
2248
                            $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...
2249
                            $assoc,
2250
                            $relatedEntities
2251
                        );
2252
                    }
2253
2254
                    $this->doPersist($relatedEntities, $visited);
2255
                    break;
2256
2257
                default:
2258
                    // Do nothing
2259
            }
2260
        }
2261
    }
2262
2263
    /**
2264
     * Cascades the delete operation to associated entities.
2265
     *
2266
     * @param object $entity
2267
     * @param array  $visited
2268
     *
2269
     * @return void
2270
     */
2271
    private function cascadeRemove($entity, array &$visited)
2272
    {
2273
        $class = $this->em->getClassMetadata(get_class($entity));
2274
2275
        $associationMappings = array_filter(
2276
            $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...
2277
            function ($assoc) { return $assoc['isCascadeRemove']; }
2278
        );
2279
2280
        $entitiesToCascade = array();
2281
2282
        foreach ($associationMappings as $assoc) {
2283
            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...
2284
                $entity->__load();
2285
            }
2286
2287
            $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...
2288
2289
            switch (true) {
2290
                case ($relatedEntities instanceof Collection):
2291
                case (is_array($relatedEntities)):
2292
                    // If its a PersistentCollection initialization is intended! No unwrap!
2293
                    foreach ($relatedEntities as $relatedEntity) {
2294
                        $entitiesToCascade[] = $relatedEntity;
2295
                    }
2296
                    break;
2297
2298
                case ($relatedEntities !== null):
2299
                    $entitiesToCascade[] = $relatedEntities;
2300
                    break;
2301
2302
                default:
2303
                    // Do nothing
2304
            }
2305
        }
2306
2307
        foreach ($entitiesToCascade as $relatedEntity) {
2308
            $this->doRemove($relatedEntity, $visited);
2309
        }
2310
    }
2311
2312
    /**
2313
     * Acquire a lock on the given entity.
2314
     *
2315
     * @param object $entity
2316
     * @param int    $lockMode
2317
     * @param int    $lockVersion
2318
     *
2319
     * @return void
2320
     *
2321
     * @throws ORMInvalidArgumentException
2322
     * @throws TransactionRequiredException
2323
     * @throws OptimisticLockException
2324
     */
2325
    public function lock($entity, $lockMode, $lockVersion = null)
2326
    {
2327
        if ($entity === null) {
2328
            throw new \InvalidArgumentException("No entity passed to UnitOfWork#lock().");
2329
        }
2330
2331
        if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) {
2332
            throw ORMInvalidArgumentException::entityNotManaged($entity);
2333
        }
2334
2335
        $class = $this->em->getClassMetadata(get_class($entity));
2336
2337
        switch (true) {
2338
            case LockMode::OPTIMISTIC === $lockMode:
2339
                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...
2340
                    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...
2341
                }
2342
2343
                if ($lockVersion === null) {
2344
                    return;
2345
                }
2346
2347
                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...
2348
                    $entity->__load();
2349
                }
2350
2351
                $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...
2352
2353
                if ($entityVersion != $lockVersion) {
2354
                    throw OptimisticLockException::lockFailedVersionMismatch($entity, $lockVersion, $entityVersion);
2355
                }
2356
2357
                break;
2358
2359
            case LockMode::NONE === $lockMode:
2360
            case LockMode::PESSIMISTIC_READ === $lockMode:
2361
            case LockMode::PESSIMISTIC_WRITE === $lockMode:
2362
                if (!$this->em->getConnection()->isTransactionActive()) {
2363
                    throw TransactionRequiredException::transactionRequired();
2364
                }
2365
2366
                $oid = spl_object_hash($entity);
2367
2368
                $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...
2369
                    array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
2370
                    $lockMode
2371
                );
2372
                break;
2373
2374
            default:
2375
                // Do nothing
2376
        }
2377
    }
2378
2379
    /**
2380
     * Gets the CommitOrderCalculator used by the UnitOfWork to order commits.
2381
     *
2382
     * @return \Doctrine\ORM\Internal\CommitOrderCalculator
2383
     */
2384
    public function getCommitOrderCalculator()
2385
    {
2386
        return new Internal\CommitOrderCalculator();
2387
    }
2388
2389
    /**
2390
     * Clears the UnitOfWork.
2391
     *
2392
     * @param string|null $entityName if given, only entities of this type will get detached.
2393
     *
2394
     * @return void
2395
     */
2396
    public function clear($entityName = null)
2397
    {
2398
        if ($entityName === null) {
2399
            $this->identityMap =
2400
            $this->entityIdentifiers =
2401
            $this->originalEntityData =
2402
            $this->entityChangeSets =
2403
            $this->entityStates =
2404
            $this->scheduledForSynchronization =
2405
            $this->entityInsertions =
2406
            $this->entityUpdates =
2407
            $this->entityDeletions =
2408
            $this->collectionDeletions =
2409
            $this->collectionUpdates =
2410
            $this->extraUpdates =
2411
            $this->readOnlyObjects =
2412
            $this->visitedCollections =
2413
            $this->orphanRemovals = array();
2414
        } else {
2415
            $visited = array();
2416
2417
            foreach ($this->identityMap as $className => $entities) {
2418
                if ($className !== $entityName) {
2419
                    continue;
2420
                }
2421
2422
                foreach ($entities as $entity) {
2423
                    $this->doDetach($entity, $visited, false);
2424
                }
2425
            }
2426
        }
2427
2428
        if ($this->evm->hasListeners(Events::onClear)) {
2429
            $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em, $entityName));
2430
        }
2431
    }
2432
2433
    /**
2434
     * INTERNAL:
2435
     * Schedules an orphaned entity for removal. The remove() operation will be
2436
     * invoked on that entity at the beginning of the next commit of this
2437
     * UnitOfWork.
2438
     *
2439
     * @ignore
2440
     *
2441
     * @param object $entity
2442
     *
2443
     * @return void
2444
     */
2445
    public function scheduleOrphanRemoval($entity)
2446
    {
2447
        $this->orphanRemovals[spl_object_hash($entity)] = $entity;
2448
    }
2449
2450
    /**
2451
     * INTERNAL:
2452
     * Cancels a previously scheduled orphan removal.
2453
     *
2454
     * @ignore
2455
     *
2456
     * @param object $entity
2457
     *
2458
     * @return void
2459
     */
2460
    public function cancelOrphanRemoval($entity)
2461
    {
2462
        unset($this->orphanRemovals[spl_object_hash($entity)]);
2463
    }
2464
2465
    /**
2466
     * INTERNAL:
2467
     * Schedules a complete collection for removal when this UnitOfWork commits.
2468
     *
2469
     * @param PersistentCollection $coll
2470
     *
2471
     * @return void
2472
     */
2473
    public function scheduleCollectionDeletion(PersistentCollection $coll)
2474
    {
2475
        $coid = spl_object_hash($coll);
2476
2477
        // TODO: if $coll is already scheduled for recreation ... what to do?
2478
        // Just remove $coll from the scheduled recreations?
2479
        if (isset($this->collectionUpdates[$coid])) {
2480
            unset($this->collectionUpdates[$coid]);
2481
        }
2482
2483
        $this->collectionDeletions[$coid] = $coll;
2484
    }
2485
2486
    /**
2487
     * @param PersistentCollection $coll
2488
     *
2489
     * @return bool
2490
     */
2491
    public function isCollectionScheduledForDeletion(PersistentCollection $coll)
2492
    {
2493
        return isset($this->collectionDeletions[spl_object_hash($coll)]);
2494
    }
2495
2496
    /**
2497
     * @param ClassMetadata $class
2498
     *
2499
     * @return \Doctrine\Common\Persistence\ObjectManagerAware|object
2500
     */
2501
    private function newInstance($class)
2502
    {
2503
        $entity = $class->newInstance();
2504
2505
        if ($entity instanceof \Doctrine\Common\Persistence\ObjectManagerAware) {
2506
            $entity->injectObjectManager($this->em, $class);
2507
        }
2508
2509
        return $entity;
2510
    }
2511
2512
    /**
2513
     * INTERNAL:
2514
     * Creates an entity. Used for reconstitution of persistent entities.
2515
     *
2516
     * Internal note: Highly performance-sensitive method.
2517
     *
2518
     * @ignore
2519
     *
2520
     * @param string $className The name of the entity class.
2521
     * @param array  $data      The data for the entity.
2522
     * @param array  $hints     Any hints to account for during reconstitution/lookup of the entity.
2523
     *
2524
     * @return object The managed entity instance.
2525
     *
2526
     * @todo Rename: getOrCreateEntity
2527
     */
2528
    public function createEntity($className, array $data, &$hints = array())
2529
    {
2530
        $class = $this->em->getClassMetadata($className);
2531
        //$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...
2532
2533
        $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...
2534
        $idHash = implode(' ', $id);
2535
2536
        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...
2537
            $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...
2538
            $oid = spl_object_hash($entity);
2539
2540
            if (
2541
                isset($hints[Query::HINT_REFRESH])
2542
                && isset($hints[Query::HINT_REFRESH_ENTITY])
2543
                && ($unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY]) !== $entity
2544
                && $unmanagedProxy instanceof Proxy
2545
                && $this->isIdentifierEquals($unmanagedProxy, $entity)
2546
            ) {
2547
                // DDC-1238 - we have a managed instance, but it isn't the provided one.
2548
                // Therefore we clear its identifier. Also, we must re-fetch metadata since the
2549
                // refreshed object may be anything
2550
2551
                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...
2552
                    $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...
2553
                }
2554
2555
                return $unmanagedProxy;
2556
            }
2557
2558
            if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
2559
                $entity->__setInitialized(true);
2560
2561
                $overrideLocalValues = true;
2562
2563
                if ($entity instanceof NotifyPropertyChanged) {
2564
                    $entity->addPropertyChangedListener($this);
2565
                }
2566
            } else {
2567
                $overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
2568
2569
                // If only a specific entity is set to refresh, check that it's the one
2570
                if (isset($hints[Query::HINT_REFRESH_ENTITY])) {
2571
                    $overrideLocalValues = $hints[Query::HINT_REFRESH_ENTITY] === $entity;
2572
                }
2573
            }
2574
2575
            if ($overrideLocalValues) {
2576
                // inject ObjectManager upon refresh.
2577
                if ($entity instanceof ObjectManagerAware) {
2578
                    $entity->injectObjectManager($this->em, $class);
2579
                }
2580
2581
                $this->originalEntityData[$oid] = $data;
2582
            }
2583
        } else {
2584
            $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...
2585
            $oid    = spl_object_hash($entity);
2586
2587
            $this->entityIdentifiers[$oid]  = $id;
2588
            $this->entityStates[$oid]       = self::STATE_MANAGED;
2589
            $this->originalEntityData[$oid] = $data;
2590
2591
            $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...
2592
2593
            if ($entity instanceof NotifyPropertyChanged) {
2594
                $entity->addPropertyChangedListener($this);
2595
            }
2596
2597
            $overrideLocalValues = true;
2598
        }
2599
2600
        if ( ! $overrideLocalValues) {
2601
            return $entity;
2602
        }
2603
2604
        foreach ($data as $field => $value) {
2605
            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...
2606
                $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...
2607
            }
2608
        }
2609
2610
        // Loading the entity right here, if its in the eager loading map get rid of it there.
2611
        unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
2612
2613
        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...
2614
            unset($this->eagerLoadingEntities[$class->rootEntityName]);
2615
        }
2616
2617
        // Properly initialize any unfetched associations, if partial objects are not allowed.
2618
        if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
2619
            return $entity;
2620
        }
2621
2622
        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...
2623
            // Check if the association is not among the fetch-joined associations already.
2624
            if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) {
2625
                continue;
2626
            }
2627
2628
            $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
2629
2630
            switch (true) {
2631
                case ($assoc['type'] & ClassMetadata::TO_ONE):
2632
                    if ( ! $assoc['isOwningSide']) {
2633
2634
                        // use the given entity association
2635
                        if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2636
2637
                            $this->originalEntityData[$oid][$field] = $data[$field];
2638
2639
                            $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...
2640
                            $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...
2641
2642
                            continue 2;
2643
                        }
2644
2645
                        // Inverse side of x-to-one can never be lazy
2646
                        $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...
2647
2648
                        continue 2;
2649
                    }
2650
2651
                    // use the entity association
2652
                    if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2653
                        $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...
2654
                        $this->originalEntityData[$oid][$field] = $data[$field];
2655
2656
                        continue;
2657
                    }
2658
2659
                    $associatedId = array();
2660
2661
                    // TODO: Is this even computed right in all cases of composite keys?
2662
                    foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
2663
                        $joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
2664
2665
                        if ($joinColumnValue !== null) {
2666
                            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...
2667
                                $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
2668
                            } else {
2669
                                $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...
2670
                            }
2671
                        } 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...
2672
                            && 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...
2673
                        ) {
2674
                            // the missing key is part of target's entity primary key
2675
                            $associatedId = array();
2676
                            break;
2677
                        }
2678
                    }
2679
2680
                    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...
2681
                        // Foreign key is NULL
2682
                        $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...
2683
                        $this->originalEntityData[$oid][$field] = null;
2684
2685
                        continue;
2686
                    }
2687
2688
                    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...
2689
                        $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...
2690
                    }
2691
2692
                    // Foreign key is set
2693
                    // Check identity map first
2694
                    // FIXME: Can break easily with composite keys if join column values are in
2695
                    //        wrong order. The correct order is the one in ClassMetadata#identifier.
2696
                    $relatedIdHash = implode(' ', $associatedId);
2697
2698
                    switch (true) {
2699
                        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...
2700
                            $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...
2701
2702
                            // If this is an uninitialized proxy, we are deferring eager loads,
2703
                            // this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
2704
                            // then we can append this entity for eager loading!
2705
                            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...
2706
                                isset($hints[self::HINT_DEFEREAGERLOAD]) &&
2707
                                !$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...
2708
                                $newValue instanceof Proxy &&
2709
                                $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...
2710
2711
                                $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...
2712
                            }
2713
2714
                            break;
2715
2716
                        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...
2717
                            // If it might be a subtype, it can not be lazy. There isn't even
2718
                            // a way to solve this with deferred eager loading, which means putting
2719
                            // an entity with subclasses at a *-to-one location is really bad! (performance-wise)
2720
                            $newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, $associatedId);
2721
                            break;
2722
2723
                        default:
2724
                            switch (true) {
2725
                                // We are negating the condition here. Other cases will assume it is valid!
2726
                                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...
2727
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2728
                                    break;
2729
2730
                                // Deferred eager load only works for single identifier classes
2731
                                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...
2732
                                    // TODO: Is there a faster approach?
2733
                                    $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...
2734
2735
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2736
                                    break;
2737
2738
                                default:
2739
                                    // TODO: This is very imperformant, ignore it?
2740
                                    $newValue = $this->em->find($assoc['targetEntity'], $associatedId);
2741
                                    break;
2742
                            }
2743
2744
                            // PERF: Inlined & optimized code from UnitOfWork#registerManaged()
2745
                            $newValueOid = spl_object_hash($newValue);
2746
                            $this->entityIdentifiers[$newValueOid] = $associatedId;
2747
                            $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...
2748
2749
                            if (
2750
                                $newValue instanceof NotifyPropertyChanged &&
2751
                                ( ! $newValue instanceof Proxy || $newValue->__isInitialized())
2752
                            ) {
2753
                                $newValue->addPropertyChangedListener($this);
2754
                            }
2755
                            $this->entityStates[$newValueOid] = self::STATE_MANAGED;
2756
                            // make sure that when an proxy is then finally loaded, $this->originalEntityData is set also!
2757
                            break;
2758
                    }
2759
2760
                    $this->originalEntityData[$oid][$field] = $newValue;
2761
                    $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...
2762
2763
                    if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
2764
                        $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...
2765
                        $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...
2766
                    }
2767
2768
                    break;
2769
2770
                default:
2771
                    // Ignore if its a cached collection
2772
                    if (isset($hints[Query::HINT_CACHE_ENABLED]) && $class->getFieldValue($entity, $field) instanceof PersistentCollection) {
2773
                        break;
2774
                    }
2775
2776
                    // use the given collection
2777
                    if (isset($data[$field]) && $data[$field] instanceof PersistentCollection) {
2778
2779
                        $data[$field]->setOwner($entity, $assoc);
2780
2781
                        $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...
2782
                        $this->originalEntityData[$oid][$field] = $data[$field];
2783
2784
                        break;
2785
                    }
2786
2787
                    // Inject collection
2788
                    $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...
2789
                    $pColl->setOwner($entity, $assoc);
2790
                    $pColl->setInitialized(false);
2791
2792
                    $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...
2793
                    $reflField->setValue($entity, $pColl);
2794
2795
                    if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
2796
                        $this->loadCollection($pColl);
2797
                        $pColl->takeSnapshot();
2798
                    }
2799
2800
                    $this->originalEntityData[$oid][$field] = $pColl;
2801
                    break;
2802
            }
2803
        }
2804
2805
        if ($overrideLocalValues) {
2806
            // defer invoking of postLoad event to hydration complete step
2807
            $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...
2808
        }
2809
2810
        return $entity;
2811
    }
2812
2813
    /**
2814
     * @return void
2815
     */
2816
    public function triggerEagerLoads()
2817
    {
2818
        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...
2819
            return;
2820
        }
2821
2822
        // avoid infinite recursion
2823
        $eagerLoadingEntities       = $this->eagerLoadingEntities;
2824
        $this->eagerLoadingEntities = array();
2825
2826
        foreach ($eagerLoadingEntities as $entityName => $ids) {
2827
            if ( ! $ids) {
2828
                continue;
2829
            }
2830
2831
            $class = $this->em->getClassMetadata($entityName);
2832
2833
            $this->getEntityPersister($entityName)->loadAll(
2834
                array_combine($class->identifier, array(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...
2835
            );
2836
        }
2837
    }
2838
2839
    /**
2840
     * Initializes (loads) an uninitialized persistent collection of an entity.
2841
     *
2842
     * @param \Doctrine\ORM\PersistentCollection $collection The collection to initialize.
2843
     *
2844
     * @return void
2845
     *
2846
     * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733.
2847
     */
2848
    public function loadCollection(PersistentCollection $collection)
2849
    {
2850
        $assoc     = $collection->getMapping();
2851
        $persister = $this->getEntityPersister($assoc['targetEntity']);
2852
2853
        switch ($assoc['type']) {
2854
            case ClassMetadata::ONE_TO_MANY:
2855
                $persister->loadOneToManyCollection($assoc, $collection->getOwner(), $collection);
2856
                break;
2857
2858
            case ClassMetadata::MANY_TO_MANY:
2859
                $persister->loadManyToManyCollection($assoc, $collection->getOwner(), $collection);
2860
                break;
2861
        }
2862
2863
        $collection->setInitialized(true);
2864
    }
2865
2866
    /**
2867
     * Gets the identity map of the UnitOfWork.
2868
     *
2869
     * @return array
2870
     */
2871
    public function getIdentityMap()
2872
    {
2873
        return $this->identityMap;
2874
    }
2875
2876
    /**
2877
     * Gets the original data of an entity. The original data is the data that was
2878
     * present at the time the entity was reconstituted from the database.
2879
     *
2880
     * @param object $entity
2881
     *
2882
     * @return array
2883
     */
2884
    public function getOriginalEntityData($entity)
2885
    {
2886
        $oid = spl_object_hash($entity);
2887
2888
        if (isset($this->originalEntityData[$oid])) {
2889
            return $this->originalEntityData[$oid];
2890
        }
2891
2892
        return array();
2893
    }
2894
2895
    /**
2896
     * @ignore
2897
     *
2898
     * @param object $entity
2899
     * @param array  $data
2900
     *
2901
     * @return void
2902
     */
2903
    public function setOriginalEntityData($entity, array $data)
2904
    {
2905
        $this->originalEntityData[spl_object_hash($entity)] = $data;
2906
    }
2907
2908
    /**
2909
     * INTERNAL:
2910
     * Sets a property value of the original data array of an entity.
2911
     *
2912
     * @ignore
2913
     *
2914
     * @param string $oid
2915
     * @param string $property
2916
     * @param mixed  $value
2917
     *
2918
     * @return void
2919
     */
2920
    public function setOriginalEntityProperty($oid, $property, $value)
2921
    {
2922
        $this->originalEntityData[$oid][$property] = $value;
2923
    }
2924
2925
    /**
2926
     * Gets the identifier of an entity.
2927
     * The returned value is always an array of identifier values. If the entity
2928
     * has a composite identifier then the identifier values are in the same
2929
     * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames().
2930
     *
2931
     * @param object $entity
2932
     *
2933
     * @return array The identifier values.
2934
     */
2935
    public function getEntityIdentifier($entity)
2936
    {
2937
        return $this->entityIdentifiers[spl_object_hash($entity)];
2938
    }
2939
2940
    /**
2941
     * Processes an entity instance to extract their identifier values.
2942
     *
2943
     * @param object $entity The entity instance.
2944
     *
2945
     * @return mixed A scalar value.
2946
     *
2947
     * @throws \Doctrine\ORM\ORMInvalidArgumentException
2948
     */
2949
    public function getSingleIdentifierValue($entity)
2950
    {
2951
        $class = $this->em->getClassMetadata(get_class($entity));
2952
2953
        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...
2954
            throw ORMInvalidArgumentException::invalidCompositeIdentifier();
2955
        }
2956
2957
        $values = $this->isInIdentityMap($entity)
2958
            ? $this->getEntityIdentifier($entity)
2959
            : $class->getIdentifierValues($entity);
2960
2961
        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...
2962
    }
2963
2964
    /**
2965
     * Tries to find an entity with the given identifier in the identity map of
2966
     * this UnitOfWork.
2967
     *
2968
     * @param mixed  $id            The entity identifier to look for.
2969
     * @param string $rootClassName The name of the root class of the mapped entity hierarchy.
2970
     *
2971
     * @return object|bool Returns the entity with the specified identifier if it exists in
2972
     *                     this UnitOfWork, FALSE otherwise.
2973
     */
2974 View Code Duplication
    public function tryGetById($id, $rootClassName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
2975
    {
2976
        $idHash = implode(' ', (array) $id);
2977
2978
        if (isset($this->identityMap[$rootClassName][$idHash])) {
2979
            return $this->identityMap[$rootClassName][$idHash];
2980
        }
2981
2982
        return false;
2983
    }
2984
2985
    /**
2986
     * Schedules an entity for dirty-checking at commit-time.
2987
     *
2988
     * @param object $entity The entity to schedule for dirty-checking.
2989
     *
2990
     * @return void
2991
     *
2992
     * @todo Rename: scheduleForSynchronization
2993
     */
2994
    public function scheduleForDirtyCheck($entity)
2995
    {
2996
        $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...
2997
2998
        $this->scheduledForSynchronization[$rootClassName][spl_object_hash($entity)] = $entity;
2999
    }
3000
3001
    /**
3002
     * Checks whether the UnitOfWork has any pending insertions.
3003
     *
3004
     * @return boolean TRUE if this UnitOfWork has pending insertions, FALSE otherwise.
3005
     */
3006
    public function hasPendingInsertions()
3007
    {
3008
        return ! empty($this->entityInsertions);
3009
    }
3010
3011
    /**
3012
     * Calculates the size of the UnitOfWork. The size of the UnitOfWork is the
3013
     * number of entities in the identity map.
3014
     *
3015
     * @return integer
3016
     */
3017
    public function size()
3018
    {
3019
        $countArray = array_map(function ($item) { return count($item); }, $this->identityMap);
3020
3021
        return array_sum($countArray);
3022
    }
3023
3024
    /**
3025
     * Gets the EntityPersister for an Entity.
3026
     *
3027
     * @param string $entityName The name of the Entity.
3028
     *
3029
     * @return \Doctrine\ORM\Persisters\Entity\EntityPersister
3030
     */
3031
    public function getEntityPersister($entityName)
3032
    {
3033
        if (isset($this->persisters[$entityName])) {
3034
            return $this->persisters[$entityName];
3035
        }
3036
3037
        $class = $this->em->getClassMetadata($entityName);
3038
3039
        switch (true) {
3040
            case ($class->isInheritanceTypeNone()):
3041
                $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...
3042
                break;
3043
3044
            case ($class->isInheritanceTypeSingleTable()):
3045
                $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...
3046
                break;
3047
3048
            case ($class->isInheritanceTypeJoined()):
3049
                $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...
3050
                break;
3051
3052
            default:
3053
                throw new \RuntimeException('No persister found for entity.');
3054
        }
3055
3056
        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...
3057
            $persister = $this->em->getConfiguration()
3058
                ->getSecondLevelCacheConfiguration()
3059
                ->getCacheFactory()
3060
                ->buildCachedEntityPersister($this->em, $persister, $class);
3061
        }
3062
3063
        $this->persisters[$entityName] = $persister;
3064
3065
        return $this->persisters[$entityName];
3066
    }
3067
3068
    /**
3069
     * Gets a collection persister for a collection-valued association.
3070
     *
3071
     * @param array $association
3072
     *
3073
     * @return \Doctrine\ORM\Persisters\Collection\CollectionPersister
3074
     */
3075
    public function getCollectionPersister(array $association)
3076
    {
3077
        $role = isset($association['cache'])
3078
            ? $association['sourceEntity'] . '::' . $association['fieldName']
3079
            : $association['type'];
3080
3081
        if (isset($this->collectionPersisters[$role])) {
3082
            return $this->collectionPersisters[$role];
3083
        }
3084
3085
        $persister = ClassMetadata::ONE_TO_MANY === $association['type']
3086
            ? new OneToManyPersister($this->em)
3087
            : new ManyToManyPersister($this->em);
3088
3089
        if ($this->hasCache && isset($association['cache'])) {
3090
            $persister = $this->em->getConfiguration()
3091
                ->getSecondLevelCacheConfiguration()
3092
                ->getCacheFactory()
3093
                ->buildCachedCollectionPersister($this->em, $persister, $association);
3094
        }
3095
3096
        $this->collectionPersisters[$role] = $persister;
3097
3098
        return $this->collectionPersisters[$role];
3099
    }
3100
3101
    /**
3102
     * INTERNAL:
3103
     * Registers an entity as managed.
3104
     *
3105
     * @param object $entity The entity.
3106
     * @param array  $id     The identifier values.
3107
     * @param array  $data   The original entity data.
3108
     *
3109
     * @return void
3110
     */
3111
    public function registerManaged($entity, array $id, array $data)
3112
    {
3113
        $oid = spl_object_hash($entity);
3114
3115
        $this->entityIdentifiers[$oid]  = $id;
3116
        $this->entityStates[$oid]       = self::STATE_MANAGED;
3117
        $this->originalEntityData[$oid] = $data;
3118
3119
        $this->addToIdentityMap($entity);
3120
3121
        if ($entity instanceof NotifyPropertyChanged && ( ! $entity instanceof Proxy || $entity->__isInitialized())) {
3122
            $entity->addPropertyChangedListener($this);
3123
        }
3124
    }
3125
3126
    /**
3127
     * INTERNAL:
3128
     * Clears the property changeset of the entity with the given OID.
3129
     *
3130
     * @param string $oid The entity's OID.
3131
     *
3132
     * @return void
3133
     */
3134
    public function clearEntityChangeSet($oid)
3135
    {
3136
        if (isset($this->entityChangeSets[$oid])) {
3137
            unset($this->entityChangeSets[$oid]);
3138
        }
3139
    }
3140
3141
    /**
3142
     * @param $class
3143
     * @param string $oid
3144
     */
3145
    public function clearScheduledForSynchronization($class, $oid) {
3146
        if (isset($this->scheduledForSynchronization[$class->rootEntityName][$oid])) {
3147
            unset($this->scheduledForSynchronization[$class->rootEntityName][$oid]);
3148
        }
3149
    }
3150
3151
    /* PropertyChangedListener implementation */
3152
3153
    /**
3154
     * Notifies this UnitOfWork of a property change in an entity.
3155
     *
3156
     * @param object $entity       The entity that owns the property.
3157
     * @param string $propertyName The name of the property that changed.
3158
     * @param mixed  $oldValue     The old value of the property.
3159
     * @param mixed  $newValue     The new value of the property.
3160
     *
3161
     * @return void
3162
     */
3163
    public function propertyChanged($entity, $propertyName, $oldValue, $newValue)
3164
    {
3165
        $oid   = spl_object_hash($entity);
3166
        $class = $this->em->getClassMetadata(get_class($entity));
3167
3168
        $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...
3169
3170
        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...
3171
            return; // ignore non-persistent fields
3172
        }
3173
3174
        // Update changeset and mark entity for synchronization
3175
        $this->entityChangeSets[$oid][$propertyName] = array($oldValue, $newValue);
3176
3177
        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...
3178
            $this->scheduleForDirtyCheck($entity);
3179
        }
3180
    }
3181
3182
    /**
3183
     * Gets the currently scheduled entity insertions in this UnitOfWork.
3184
     *
3185
     * @return array
3186
     */
3187
    public function getScheduledEntityInsertions()
3188
    {
3189
        return $this->entityInsertions;
3190
    }
3191
3192
    /**
3193
     * Gets the currently scheduled entity updates in this UnitOfWork.
3194
     *
3195
     * @return array
3196
     */
3197
    public function getScheduledEntityUpdates()
3198
    {
3199
        return $this->entityUpdates;
3200
    }
3201
3202
    /**
3203
     * Gets the currently scheduled entity deletions in this UnitOfWork.
3204
     *
3205
     * @return array
3206
     */
3207
    public function getScheduledEntityDeletions()
3208
    {
3209
        return $this->entityDeletions;
3210
    }
3211
3212
    /**
3213
     * Gets the currently scheduled complete collection deletions
3214
     *
3215
     * @return array
3216
     */
3217
    public function getScheduledCollectionDeletions()
3218
    {
3219
        return $this->collectionDeletions;
3220
    }
3221
3222
    /**
3223
     * Gets the currently scheduled collection inserts, updates and deletes.
3224
     *
3225
     * @return array
3226
     */
3227
    public function getScheduledCollectionUpdates()
3228
    {
3229
        return $this->collectionUpdates;
3230
    }
3231
3232
    /**
3233
     * Helper method to initialize a lazy loading proxy or persistent collection.
3234
     *
3235
     * @param object $obj
3236
     *
3237
     * @return void
3238
     */
3239
    public function initializeObject($obj)
3240
    {
3241
        if ($obj instanceof Proxy) {
3242
            $obj->__load();
3243
3244
            return;
3245
        }
3246
3247
        if ($obj instanceof PersistentCollection) {
3248
            $obj->initialize();
3249
        }
3250
    }
3251
3252
    /**
3253
     * Helper method to show an object as string.
3254
     *
3255
     * @param object $obj
3256
     *
3257
     * @return string
3258
     */
3259
    private static function objToStr($obj)
3260
    {
3261
        return method_exists($obj, '__toString') ? (string) $obj : get_class($obj).'@'.spl_object_hash($obj);
3262
    }
3263
3264
    /**
3265
     * Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit().
3266
     *
3267
     * This operation cannot be undone as some parts of the UnitOfWork now keep gathering information
3268
     * on this object that might be necessary to perform a correct update.
3269
     *
3270
     * @param object $object
3271
     *
3272
     * @return void
3273
     *
3274
     * @throws ORMInvalidArgumentException
3275
     */
3276
    public function markReadOnly($object)
3277
    {
3278
        if ( ! is_object($object) || ! $this->isInIdentityMap($object)) {
3279
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3280
        }
3281
3282
        $this->readOnlyObjects[spl_object_hash($object)] = true;
3283
    }
3284
3285
    /**
3286
     * Is this entity read only?
3287
     *
3288
     * @param object $object
3289
     *
3290
     * @return bool
3291
     *
3292
     * @throws ORMInvalidArgumentException
3293
     */
3294
    public function isReadOnly($object)
3295
    {
3296
        if ( ! is_object($object)) {
3297
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3298
        }
3299
3300
        return isset($this->readOnlyObjects[spl_object_hash($object)]);
3301
    }
3302
3303
    /**
3304
     * Perform whatever processing is encapsulated here after completion of the transaction.
3305
     */
3306
    private function afterTransactionComplete()
3307
    {
3308
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3309
            $persister->afterTransactionComplete();
3310
        });
3311
    }
3312
3313
    /**
3314
     * Perform whatever processing is encapsulated here after completion of the rolled-back.
3315
     */
3316
    private function afterTransactionRolledBack()
3317
    {
3318
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3319
            $persister->afterTransactionRolledBack();
3320
        });
3321
    }
3322
3323
    /**
3324
     * Performs an action after the transaction.
3325
     *
3326
     * @param callable $callback
3327
     */
3328
    private function performCallbackOnCachedPersister(callable $callback)
3329
    {
3330
        if ( ! $this->hasCache) {
3331
            return;
3332
        }
3333
3334
        foreach (array_merge($this->persisters, $this->collectionPersisters) as $persister) {
3335
            if ($persister instanceof CachedPersister) {
3336
                $callback($persister);
3337
            }
3338
        }
3339
    }
3340
3341
    private function dispatchOnFlushEvent()
3342
    {
3343
        if ($this->evm->hasListeners(Events::onFlush)) {
3344
            $this->evm->dispatchEvent(Events::onFlush, new OnFlushEventArgs($this->em));
3345
        }
3346
    }
3347
3348
    private function dispatchPostFlushEvent()
3349
    {
3350
        if ($this->evm->hasListeners(Events::postFlush)) {
3351
            $this->evm->dispatchEvent(Events::postFlush, new PostFlushEventArgs($this->em));
3352
        }
3353
    }
3354
3355
    /**
3356
     * Verifies if two given entities actually are the same based on identifier comparison
3357
     *
3358
     * @param object $entity1
3359
     * @param object $entity2
3360
     *
3361
     * @return bool
3362
     */
3363
    private function isIdentifierEquals($entity1, $entity2)
3364
    {
3365
        if ($entity1 === $entity2) {
3366
            return true;
3367
        }
3368
3369
        $class = $this->em->getClassMetadata(get_class($entity1));
3370
3371
        if ($class !== $this->em->getClassMetadata(get_class($entity2))) {
3372
            return false;
3373
        }
3374
3375
        $oid1 = spl_object_hash($entity1);
3376
        $oid2 = spl_object_hash($entity2);
3377
3378
        $id1 = isset($this->entityIdentifiers[$oid1])
3379
            ? $this->entityIdentifiers[$oid1]
3380
            : $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...
3381
        $id2 = isset($this->entityIdentifiers[$oid2])
3382
            ? $this->entityIdentifiers[$oid2]
3383
            : $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...
3384
3385
        return $id1 === $id2 || implode(' ', $id1) === implode(' ', $id2);
3386
    }
3387
3388
    /**
3389
     * @param object $entity
3390
     * @param object $managedCopy
3391
     *
3392
     * @throws ORMException
3393
     * @throws OptimisticLockException
3394
     * @throws TransactionRequiredException
3395
     */
3396
    private function mergeEntityStateIntoManagedCopy($entity, $managedCopy)
3397
    {
3398
        $class = $this->em->getClassMetadata(get_class($entity));
3399
3400
        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...
3401
            $name = $prop->name;
3402
3403
            $prop->setAccessible(true);
3404
3405
            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...
3406
                if ( ! $class->isIdentifier($name)) {
3407
                    $prop->setValue($managedCopy, $prop->getValue($entity));
3408
                }
3409
            } else {
3410
                $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...
3411
3412
                if ($assoc2['type'] & ClassMetadata::TO_ONE) {
3413
                    $other = $prop->getValue($entity);
3414
                    if ($other === null) {
3415
                        $prop->setValue($managedCopy, null);
3416
                    } else {
3417
                        if ($other instanceof Proxy && !$other->__isInitialized()) {
3418
                            // do not merge fields marked lazy that have not been fetched.
3419
                            continue;
3420
                        }
3421
3422
                        if ( ! $assoc2['isCascadeMerge']) {
3423
                            if ($this->getEntityState($other) === self::STATE_DETACHED) {
3424
                                $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']);
3425
                                $relatedId   = $targetClass->getIdentifierValues($other);
3426
3427
                                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...
3428
                                    $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...
3429
                                } else {
3430
                                    $other = $this->em->getProxyFactory()->getProxy(
3431
                                        $assoc2['targetEntity'],
3432
                                        $relatedId
3433
                                    );
3434
                                    $this->registerManaged($other, $relatedId, array());
3435
                                }
3436
                            }
3437
3438
                            $prop->setValue($managedCopy, $other);
3439
                        }
3440
                    }
3441
                } else {
3442
                    $mergeCol = $prop->getValue($entity);
3443
3444
                    if ($mergeCol instanceof PersistentCollection && ! $mergeCol->isInitialized()) {
3445
                        // do not merge fields marked lazy that have not been fetched.
3446
                        // keep the lazy persistent collection of the managed copy.
3447
                        continue;
3448
                    }
3449
3450
                    $managedCol = $prop->getValue($managedCopy);
3451
3452
                    if ( ! $managedCol) {
3453
                        $managedCol = new PersistentCollection(
3454
                            $this->em,
3455
                            $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...
3456
                            new ArrayCollection
3457
                        );
3458
                        $managedCol->setOwner($managedCopy, $assoc2);
3459
                        $prop->setValue($managedCopy, $managedCol);
3460
3461
                        $this->originalEntityData[spl_object_hash($entity)][$name] = $managedCol;
3462
                    }
3463
3464
                    if ($assoc2['isCascadeMerge']) {
3465
                        $managedCol->initialize();
3466
3467
                        // clear and set dirty a managed collection if its not also the same collection to merge from.
3468
                        if ( ! $managedCol->isEmpty() && $managedCol !== $mergeCol) {
3469
                            $managedCol->unwrap()->clear();
3470
                            $managedCol->setDirty(true);
3471
3472
                            if ($assoc2['isOwningSide']
3473
                                && $assoc2['type'] == ClassMetadata::MANY_TO_MANY
3474
                                && $class->isChangeTrackingNotify()
3475
                            ) {
3476
                                $this->scheduleForDirtyCheck($managedCopy);
3477
                            }
3478
                        }
3479
                    }
3480
                }
3481
            }
3482
3483
            if ($class->isChangeTrackingNotify()) {
3484
                // Just treat all properties as changed, there is no other choice.
3485
                $this->propertyChanged($managedCopy, $name, null, $prop->getValue($managedCopy));
3486
            }
3487
        }
3488
    }
3489
3490
    /**
3491
     * This method called by hydrators, and indicates that hydrator totally completed current hydration cycle.
3492
     * Unit of work able to fire deferred events, related to loading events here.
3493
     *
3494
     * @internal should be called internally from object hydrators
3495
     */
3496
    public function hydrationComplete()
3497
    {
3498
        $this->hydrationCompleteHandler->hydrationComplete();
3499
    }
3500
}
3501