Completed
Pull Request — master (#6440)
by Ondřej
21:27
created

UnitOfWork::doMerge()   C

Complexity

Conditions 12
Paths 44

Size

Total Lines 88
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 38
CRAP Score 12.1242

Importance

Changes 0
Metric Value
dl 0
loc 88
ccs 38
cts 42
cp 0.9048
rs 5.034
c 0
b 0
f 0
cc 12
eloc 45
nc 44
nop 4
crap 12.1242

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
343 23
            $this->dispatchOnFlushEvent();
344 23
            $this->dispatchPostFlushEvent();
345
346 23
            return; // Nothing to do.
347
        }
348
349 988
        if ($this->orphanRemovals) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->orphanRemovals of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
350 15
            foreach ($this->orphanRemovals as $orphan) {
351 15
                $this->remove($orphan);
352
            }
353
        }
354
355 988
        $this->dispatchOnFlushEvent();
356
357
        // Now we need a commit order to maintain referential integrity
358 988
        $commitOrder = $this->getCommitOrder();
359
360 988
        $conn = $this->em->getConnection();
361 988
        $conn->beginTransaction();
362
363
        try {
364
            // Collection deletions (deletions of complete collections)
365 988
            foreach ($this->collectionDeletions as $collectionToDelete) {
366 16
                $this->getCollectionPersister($collectionToDelete->getMapping())->delete($collectionToDelete);
367
            }
368
369 988
            if ($this->entityInsertions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->entityInsertions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
370 984
                foreach ($commitOrder as $class) {
371 984
                    $this->executeInserts($class);
372
                }
373
            }
374
375 987
            if ($this->entityUpdates) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->entityUpdates of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
376 104
                foreach ($commitOrder as $class) {
377 104
                    $this->executeUpdates($class);
378
                }
379
            }
380
381
            // Extra updates that were requested by persisters.
382 983
            if ($this->extraUpdates) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->extraUpdates of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
383 43
                $this->executeExtraUpdates();
384
            }
385
386
            // Collection updates (deleteRows, updateRows, insertRows)
387 983
            foreach ($this->collectionUpdates as $collectionToUpdate) {
388 520
                $this->getCollectionPersister($collectionToUpdate->getMapping())->update($collectionToUpdate);
389
            }
390
391
            // Entity deletions come last and need to be in reverse commit order
392 983
            if ($this->entityDeletions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->entityDeletions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

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

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

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

Loading history...
394 53
                    $this->executeDeletions($commitOrder[$i]);
395
                }
396
            }
397
398 983
            $conn->commit();
399 15
        } catch (Exception $e) {
400 15
            $this->em->close();
401 15
            $conn->rollBack();
402
403 15
            $this->afterTransactionRolledBack();
404
405 15
            throw $e;
406
        }
407
408 983
        $this->afterTransactionComplete();
409
410
        // Take new snapshots from visited collections
411 983
        foreach ($this->visitedCollections as $coll) {
412 518
            $coll->takeSnapshot();
413
        }
414
415 983
        $this->dispatchPostFlushEvent();
416
417
        // Clear up
418 982
        $this->entityInsertions =
419 982
        $this->entityUpdates =
420 982
        $this->entityDeletions =
421 982
        $this->extraUpdates =
422 982
        $this->entityChangeSets =
423 982
        $this->collectionUpdates =
424 982
        $this->collectionDeletions =
425 982
        $this->visitedCollections =
426 982
        $this->scheduledForSynchronization =
427 982
        $this->orphanRemovals = [];
428 982
    }
429
430
    /**
431
     * Computes the changesets of all entities scheduled for insertion.
432
     *
433
     * @return void
434
     */
435 992
    private function computeScheduleInsertsChangeSets()
436
    {
437 992
        foreach ($this->entityInsertions as $entity) {
438 984
            $class = $this->em->getClassMetadata(get_class($entity));
439
440 984
            $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...
441
        }
442 992
    }
443
444
    /**
445
     * Only flushes the given entity according to a ruleset that keeps the UoW consistent.
446
     *
447
     * 1. All entities scheduled for insertion, (orphan) removals and changes in collections are processed as well!
448
     * 2. Read Only entities are skipped.
449
     * 3. Proxies are skipped.
450
     * 4. Only if entity is properly managed.
451
     *
452
     * @param object $entity
453
     *
454
     * @return void
455
     *
456
     * @throws \InvalidArgumentException
457
     */
458 16
    private function computeSingleEntityChangeSet($entity)
459
    {
460 16
        $state = $this->getEntityState($entity);
461
462 16
        if ($state !== self::STATE_MANAGED && $state !== self::STATE_REMOVED) {
463 1
            throw new \InvalidArgumentException("Entity has to be managed or scheduled for removal for single computation " . self::objToStr($entity));
464
        }
465
466 15
        $class = $this->em->getClassMetadata(get_class($entity));
467
468 15
        if ($state === self::STATE_MANAGED && $class->isChangeTrackingDeferredImplicit()) {
469 14
            $this->persist($entity);
470
        }
471
472
        // Compute changes for INSERTed entities first. This must always happen even in this case.
473 15
        $this->computeScheduleInsertsChangeSets();
474
475 15
        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...
476
            return;
477
        }
478
479
        // Ignore uninitialized proxy objects
480 15
        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...
481 2
            return;
482
        }
483
484
        // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here.
485 13
        $oid = spl_object_hash($entity);
486
487 13
        if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) {
488 6
            $this->computeChangeSet($class, $entity);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

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

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

Loading history...
489
        }
490 12
    }
491
492
    /**
493
     * Executes any extra updates that have been scheduled.
494
     */
495 43
    private function executeExtraUpdates()
496
    {
497 43
        foreach ($this->extraUpdates as $oid => $update) {
498 43
            list ($entity, $changeset) = $update;
499
500 43
            $this->entityChangeSets[$oid] = $changeset;
501 43
            $this->getEntityPersister(get_class($entity))->update($entity);
502
        }
503
504 43
        $this->extraUpdates = [];
505 43
    }
506
507
    /**
508
     * Gets the changeset for an entity.
509
     *
510
     * @param object $entity
511
     *
512
     * @return array
513
     */
514 987
    public function & getEntityChangeSet($entity)
515
    {
516 987
        $oid  = spl_object_hash($entity);
517 987
        $data = [];
518
519 987
        if (!isset($this->entityChangeSets[$oid])) {
520 1
            return $data;
521
        }
522
523 987
        return $this->entityChangeSets[$oid];
524
    }
525
526
    /**
527
     * Computes the changes that happened to a single entity.
528
     *
529
     * Modifies/populates the following properties:
530
     *
531
     * {@link _originalEntityData}
532
     * If the entity is NEW or MANAGED but not yet fully persisted (only has an id)
533
     * then it was not fetched from the database and therefore we have no original
534
     * entity data yet. All of the current entity data is stored as the original entity data.
535
     *
536
     * {@link _entityChangeSets}
537
     * The changes detected on all properties of the entity are stored there.
538
     * A change is a tuple array where the first entry is the old value and the second
539
     * entry is the new value of the property. Changesets are used by persisters
540
     * to INSERT/UPDATE the persistent entity state.
541
     *
542
     * {@link _entityUpdates}
543
     * If the entity is already fully MANAGED (has been fetched from the database before)
544
     * and any changes to its properties are detected, then a reference to the entity is stored
545
     * there to mark it for an update.
546
     *
547
     * {@link _collectionDeletions}
548
     * If a PersistentCollection has been de-referenced in a fully MANAGED entity,
549
     * then this collection is marked for deletion.
550
     *
551
     * @ignore
552
     *
553
     * @internal Don't call from the outside.
554
     *
555
     * @param ClassMetadata $class  The class descriptor of the entity.
556
     * @param object        $entity The entity for which to compute the changes.
557
     *
558
     * @return void
559
     */
560 994
    public function computeChangeSet(ClassMetadata $class, $entity)
561
    {
562 994
        $oid = spl_object_hash($entity);
563
564 994
        if (isset($this->readOnlyObjects[$oid])) {
565 2
            return;
566
        }
567
568 994
        if ( ! $class->isInheritanceTypeNone()) {
569 308
            $class = $this->em->getClassMetadata(get_class($entity));
570
        }
571
572 994
        $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...
573
574 994
        if ($invoke !== ListenersInvoker::INVOKE_NONE) {
575 134
            $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...
576
        }
577
578 994
        $actualData = [];
579
580 994
        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...
581 994
            $value = $refProp->getValue($entity);
582
583 994
            if ($class->isCollectionValuedAssociation($name) && $value !== null) {
584 768
                if ($value instanceof PersistentCollection) {
585 185
                    if ($value->getOwner() === $entity) {
586 185
                        continue;
587
                    }
588
589 5
                    $value = new ArrayCollection($value->getValues());
590
                }
591
592
                // If $value is not a Collection then use an ArrayCollection.
593 764
                if ( ! $value instanceof Collection) {
594 239
                    $value = new ArrayCollection($value);
595
                }
596
597 764
                $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...
598
599
                // Inject PersistentCollection
600 764
                $value = new PersistentCollection(
601 764
                    $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...
602
                );
603 764
                $value->setOwner($entity, $assoc);
604 764
                $value->setDirty( ! $value->isEmpty());
605
606 764
                $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...
607
608 764
                $actualData[$name] = $value;
609
610 764
                continue;
611
            }
612
613 994
            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...
614 994
                $actualData[$name] = $value;
615
            }
616
        }
617
618 994
        if ( ! isset($this->originalEntityData[$oid])) {
619
            // Entity is either NEW or MANAGED but not yet fully persisted (only has an id).
620
            // These result in an INSERT.
621 990
            $this->originalEntityData[$oid] = $actualData;
622 990
            $changeSet = [];
623
624 990
            foreach ($actualData as $propName => $actualValue) {
625 974
                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...
626 928
                    $changeSet[$propName] = [null, $actualValue];
627
628 928
                    continue;
629
                }
630
631 879
                $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...
632
633 879
                if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
634 879
                    $changeSet[$propName] = [null, $actualValue];
635
                }
636
            }
637
638 990
            $this->entityChangeSets[$oid] = $changeSet;
639
        } else {
640
            // Entity is "fully" MANAGED: it was already fully persisted before
641
            // and we have a copy of the original data
642 243
            $originalData           = $this->originalEntityData[$oid];
643 243
            $isChangeTrackingNotify = $class->isChangeTrackingNotify();
644 243
            $changeSet              = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid]))
645
                ? $this->entityChangeSets[$oid]
646 243
                : [];
647
648 243
            foreach ($actualData as $propName => $actualValue) {
649
                // skip field, its a partially omitted one!
650 229
                if ( ! (isset($originalData[$propName]) || array_key_exists($propName, $originalData))) {
651 5
                    continue;
652
                }
653
654 229
                $orgValue = $originalData[$propName];
655
656
                // skip if value haven't changed
657 229
                if ($orgValue === $actualValue) {
658 216
                    continue;
659
                }
660
661
                // if regular field
662 103
                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...
663 55
                    if ($isChangeTrackingNotify) {
664
                        continue;
665
                    }
666
667 55
                    $changeSet[$propName] = [$orgValue, $actualValue];
668
669 55
                    continue;
670
                }
671
672 52
                $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...
673
674
                // Persistent collection was exchanged with the "originally"
675
                // created one. This can only mean it was cloned and replaced
676
                // on another entity.
677 52
                if ($actualValue instanceof PersistentCollection) {
678 7
                    $owner = $actualValue->getOwner();
679 7
                    if ($owner === null) { // cloned
680
                        $actualValue->setOwner($entity, $assoc);
681 7
                    } else if ($owner !== $entity) { // no clone, we have to fix
682
                        if (!$actualValue->isInitialized()) {
683
                            $actualValue->initialize(); // we have to do this otherwise the cols share state
684
                        }
685
                        $newValue = clone $actualValue;
686
                        $newValue->setOwner($entity, $assoc);
687
                        $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...
688
                    }
689
                }
690
691 52
                if ($orgValue instanceof PersistentCollection) {
692
                    // A PersistentCollection was de-referenced, so delete it.
693 8
                    $coid = spl_object_hash($orgValue);
694
695 8
                    if (isset($this->collectionDeletions[$coid])) {
696
                        continue;
697
                    }
698
699 8
                    $this->collectionDeletions[$coid] = $orgValue;
700 8
                    $changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored.
701
702 8
                    continue;
703
                }
704
705 44
                if ($assoc['type'] & ClassMetadata::TO_ONE) {
706 44
                    if ($assoc['isOwningSide']) {
707 19
                        $changeSet[$propName] = [$orgValue, $actualValue];
708
                    }
709
710 44
                    if ($orgValue !== null && $assoc['orphanRemoval']) {
711 44
                        $this->scheduleOrphanRemoval($orgValue);
712
                    }
713
                }
714
            }
715
716 243
            if ($changeSet) {
717 80
                $this->entityChangeSets[$oid]   = $changeSet;
718 80
                $this->originalEntityData[$oid] = $actualData;
719 80
                $this->entityUpdates[$oid]      = $entity;
720
            }
721
        }
722
723
        // Look for changes in associations of the entity
724 994
        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...
725 879
            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...
726 623
                continue;
727
            }
728
729 855
            $this->computeAssociationChanges($assoc, $val);
730
731 849
            if ( ! isset($this->entityChangeSets[$oid]) &&
732 849
                $assoc['isOwningSide'] &&
733 849
                $assoc['type'] == ClassMetadata::MANY_TO_MANY &&
734 849
                $val instanceof PersistentCollection &&
735 849
                $val->isDirty()) {
736
737 28
                $this->entityChangeSets[$oid]   = [];
738 28
                $this->originalEntityData[$oid] = $actualData;
739 849
                $this->entityUpdates[$oid]      = $entity;
740
            }
741
        }
742 988
    }
743
744
    /**
745
     * Computes all the changes that have been done to entities and collections
746
     * since the last commit and stores these changes in the _entityChangeSet map
747
     * temporarily for access by the persisters, until the UoW commit is finished.
748
     *
749
     * @return void
750
     */
751 985
    public function computeChangeSets()
752
    {
753
        // Compute changes for INSERTed entities first. This must always happen.
754 985
        $this->computeScheduleInsertsChangeSets();
755
756
        // Compute changes for other MANAGED entities. Change tracking policies take effect here.
757 985
        foreach ($this->identityMap as $className => $entities) {
758 426
            $class = $this->em->getClassMetadata($className);
759
760
            // Skip class if instances are read-only
761 426
            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...
762 1
                continue;
763
            }
764
765
            // If change tracking is explicit or happens through notification, then only compute
766
            // changes on entities of that type that are explicitly marked for synchronization.
767
            switch (true) {
768 425
                case ($class->isChangeTrackingDeferredImplicit()):
769 423
                    $entitiesToProcess = $entities;
770 423
                    break;
771
772 3
                case (isset($this->scheduledForSynchronization[$className])):
773 3
                    $entitiesToProcess = $this->scheduledForSynchronization[$className];
774 3
                    break;
775
776
                default:
777 1
                    $entitiesToProcess = [];
778
779
            }
780
781 425
            foreach ($entitiesToProcess as $entity) {
782
                // Ignore uninitialized proxy objects
783 411
                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...
784 32
                    continue;
785
                }
786
787
                // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here.
788 410
                $oid = spl_object_hash($entity);
789
790 410
                if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) {
791 425
                    $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...
792
                }
793
            }
794
        }
795 985
    }
796
797
    /**
798
     * Computes the changes of an association.
799
     *
800
     * @param array $assoc The association mapping.
801
     * @param mixed $value The value of the association.
802
     *
803
     * @throws ORMInvalidArgumentException
804
     * @throws ORMException
805
     *
806
     * @return void
807
     */
808 855
    private function computeAssociationChanges($assoc, $value)
809
    {
810 855
        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...
811 25
            return;
812
        }
813
814 854
        if ($value instanceof PersistentCollection && $value->isDirty()) {
815 521
            $coid = spl_object_hash($value);
816
817 521
            $this->collectionUpdates[$coid] = $value;
818 521
            $this->visitedCollections[$coid] = $value;
819
        }
820
821
        // Look through the entities, and in any of their associations,
822
        // for transient (new) entities, recursively. ("Persistence by reachability")
823
        // Unwrap. Uninitialized collections will simply be empty.
824 854
        $unwrappedValue = ($assoc['type'] & ClassMetadata::TO_ONE) ? [$value] : $value->unwrap();
825 854
        $targetClass    = $this->em->getClassMetadata($assoc['targetEntity']);
826
827 854
        foreach ($unwrappedValue as $key => $entry) {
828 710
            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...
829 6
                throw ORMInvalidArgumentException::invalidAssociation($targetClass, $assoc, $entry);
0 ignored issues
show
Compatibility introduced by
$targetClass of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

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

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

Loading history...
830
            }
831
832 704
            $state = $this->getEntityState($entry, self::STATE_NEW);
833
834 704
            if ( ! ($entry instanceof $assoc['targetEntity'])) {
835
                throw ORMException::unexpectedAssociationValue($assoc['sourceEntity'], $assoc['fieldName'], get_class($entry), $assoc['targetEntity']);
836
            }
837
838
            switch ($state) {
839 704
                case self::STATE_NEW:
840 33
                    if ( ! $assoc['isCascadePersist']) {
841 1
                        throw ORMInvalidArgumentException::newEntityFoundThroughRelationship($assoc, $entry);
842
                    }
843
844 32
                    $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...
845 32
                    $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...
846 32
                    break;
847
848 700
                case self::STATE_REMOVED:
849
                    // Consume the $value as array (it's either an array or an ArrayAccess)
850
                    // and remove the element from Collection.
851 4
                    if ($assoc['type'] & ClassMetadata::TO_MANY) {
852 3
                        unset($value[$key]);
853
                    }
854 4
                    break;
855
856 700
                case self::STATE_DETACHED:
857
                    // Can actually not happen right now as we assume STATE_NEW,
858
                    // so the exception will be raised from the DBAL layer (constraint violation).
859
                    throw ORMInvalidArgumentException::detachedEntityFoundThroughRelationship($assoc, $entry);
860
                    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...
861
862 703
                default:
863
                    // MANAGED associated entities are already taken into account
864
                    // during changeset calculation anyway, since they are in the identity map.
865
            }
866
        }
867 848
    }
868
869
    /**
870
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
871
     * @param object                              $entity
872
     *
873
     * @return void
874
     */
875 1013
    private function persistNew($class, $entity)
876
    {
877 1013
        $oid    = spl_object_hash($entity);
878 1013
        $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::prePersist);
879
880 1013
        if ($invoke !== ListenersInvoker::INVOKE_NONE) {
881 137
            $this->listenersInvoker->invoke($class, Events::prePersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
882
        }
883
884 1013
        $idGen = $class->idGenerator;
885
886 1013
        if ( ! $idGen->isPostInsertGenerator()) {
887 262
            $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...
888
889 262
            if ( ! $idGen instanceof \Doctrine\ORM\Id\AssignedGenerator) {
890 1
                $idValue = [$class->getSingleIdentifierFieldName() => $this->convertSingleFieldIdentifierToPHPValue($class, $idValue)];
891
892 1
                $class->setIdentifierValues($entity, $idValue);
893
            }
894
895 262
            $this->entityIdentifiers[$oid] = $idValue;
896
        }
897
898 1013
        $this->entityStates[$oid] = self::STATE_MANAGED;
899
900 1013
        $this->scheduleForInsert($entity);
901 1013
    }
902
903
    /**
904
     * INTERNAL:
905
     * Computes the changeset of an individual entity, independently of the
906
     * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit().
907
     *
908
     * The passed entity must be a managed entity. If the entity already has a change set
909
     * because this method is invoked during a commit cycle then the change sets are added.
910
     * whereby changes detected in this method prevail.
911
     *
912
     * @ignore
913
     *
914
     * @param ClassMetadata $class  The class descriptor of the entity.
915
     * @param object        $entity The entity for which to (re)calculate the change set.
916
     *
917
     * @return void
918
     *
919
     * @throws ORMInvalidArgumentException If the passed entity is not MANAGED.
920
     */
921 15
    public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity)
922
    {
923 15
        $oid = spl_object_hash($entity);
924
925 15
        if ( ! isset($this->entityStates[$oid]) || $this->entityStates[$oid] != self::STATE_MANAGED) {
926
            throw ORMInvalidArgumentException::entityNotManaged($entity);
927
        }
928
929
        // skip if change tracking is "NOTIFY"
930 15
        if ($class->isChangeTrackingNotify()) {
931
            return;
932
        }
933
934 15
        if ( ! $class->isInheritanceTypeNone()) {
935 3
            $class = $this->em->getClassMetadata(get_class($entity));
936
        }
937
938 15
        $actualData = [];
939
940 15
        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...
941 15
            if (( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity())
942 15
                && ($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...
943 15
                && ! $class->isCollectionValuedAssociation($name)) {
944 15
                $actualData[$name] = $refProp->getValue($entity);
945
            }
946
        }
947
948 15
        if ( ! isset($this->originalEntityData[$oid])) {
949
            throw new \RuntimeException('Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.');
950
        }
951
952 15
        $originalData = $this->originalEntityData[$oid];
953 15
        $changeSet = [];
954
955 15
        foreach ($actualData as $propName => $actualValue) {
956 15
            $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
957
958 15
            if ($orgValue !== $actualValue) {
959 15
                $changeSet[$propName] = [$orgValue, $actualValue];
960
            }
961
        }
962
963 15
        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...
964 7
            if (isset($this->entityChangeSets[$oid])) {
965 6
                $this->entityChangeSets[$oid] = array_merge($this->entityChangeSets[$oid], $changeSet);
966 1
            } else if ( ! isset($this->entityInsertions[$oid])) {
967 1
                $this->entityChangeSets[$oid] = $changeSet;
968 1
                $this->entityUpdates[$oid]    = $entity;
969
            }
970 7
            $this->originalEntityData[$oid] = $actualData;
971
        }
972 15
    }
973
974
    /**
975
     * Executes all entity insertions for entities of the specified type.
976
     *
977
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
978
     *
979
     * @return void
980
     */
981 984
    private function executeInserts($class)
982
    {
983 984
        $entities   = [];
984 984
        $className  = $class->name;
985 984
        $persister  = $this->getEntityPersister($className);
986 984
        $invoke     = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist);
987
988 984
        foreach ($this->entityInsertions as $oid => $entity) {
989
990 984
            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...
991 845
                continue;
992
            }
993
994 984
            $persister->addInsert($entity);
995
996 984
            unset($this->entityInsertions[$oid]);
997
998 984
            if ($invoke !== ListenersInvoker::INVOKE_NONE) {
999 984
                $entities[] = $entity;
1000
            }
1001
        }
1002
1003 984
        $postInsertIds = $persister->executeInserts();
1004
1005 984
        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...
1006
            // Persister returned post-insert IDs
1007 897
            foreach ($postInsertIds as $postInsertId) {
1008 897
                $idField = $class->getSingleIdentifierFieldName();
1009 897
                $idValue = $this->convertSingleFieldIdentifierToPHPValue($class, $postInsertId['generatedId']);
1010
1011 897
                $entity  = $postInsertId['entity'];
1012 897
                $oid     = spl_object_hash($entity);
1013
1014 897
                $class->reflFields[$idField]->setValue($entity, $idValue);
1015
1016 897
                $this->entityIdentifiers[$oid] = [$idField => $idValue];
1017 897
                $this->entityStates[$oid] = self::STATE_MANAGED;
1018 897
                $this->originalEntityData[$oid][$idField] = $idValue;
1019
1020 897
                $this->addToIdentityMap($entity);
1021
            }
1022
        }
1023
1024 984
        foreach ($entities as $entity) {
1025 133
            $this->listenersInvoker->invoke($class, Events::postPersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
1026
        }
1027 984
    }
1028
1029
    /**
1030
     * Executes all entity updates for entities of the specified type.
1031
     *
1032
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
1033
     *
1034
     * @return void
1035
     */
1036 104
    private function executeUpdates($class)
1037
    {
1038 104
        $className          = $class->name;
1039 104
        $persister          = $this->getEntityPersister($className);
1040 104
        $preUpdateInvoke    = $this->listenersInvoker->getSubscribedSystems($class, Events::preUpdate);
1041 104
        $postUpdateInvoke   = $this->listenersInvoker->getSubscribedSystems($class, Events::postUpdate);
1042
1043 104
        foreach ($this->entityUpdates as $oid => $entity) {
1044 104
            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...
1045 69
                continue;
1046
            }
1047
1048 104
            if ($preUpdateInvoke != ListenersInvoker::INVOKE_NONE) {
1049 12
                $this->listenersInvoker->invoke($class, Events::preUpdate, $entity, new PreUpdateEventArgs($entity, $this->em, $this->getEntityChangeSet($entity)), $preUpdateInvoke);
1050
1051 12
                $this->recomputeSingleEntityChangeSet($class, $entity);
1052
            }
1053
1054 104
            if ( ! empty($this->entityChangeSets[$oid])) {
1055 78
                $persister->update($entity);
1056
            }
1057
1058 100
            unset($this->entityUpdates[$oid]);
1059
1060 100
            if ($postUpdateInvoke != ListenersInvoker::INVOKE_NONE) {
1061 100
                $this->listenersInvoker->invoke($class, Events::postUpdate, $entity, new LifecycleEventArgs($entity, $this->em), $postUpdateInvoke);
1062
            }
1063
        }
1064 100
    }
1065
1066
    /**
1067
     * Executes all entity deletions for entities of the specified type.
1068
     *
1069
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
1070
     *
1071
     * @return void
1072
     */
1073 53
    private function executeDeletions($class)
1074
    {
1075 53
        $className  = $class->name;
1076 53
        $persister  = $this->getEntityPersister($className);
1077 53
        $invoke     = $this->listenersInvoker->getSubscribedSystems($class, Events::postRemove);
1078
1079 53
        foreach ($this->entityDeletions as $oid => $entity) {
1080 53
            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...
1081 18
                continue;
1082
            }
1083
1084 53
            $persister->delete($entity);
1085
1086
            unset(
1087 53
                $this->entityDeletions[$oid],
1088 53
                $this->entityIdentifiers[$oid],
1089 53
                $this->originalEntityData[$oid],
1090 53
                $this->entityStates[$oid]
1091
            );
1092
1093
            // Entity with this $oid after deletion treated as NEW, even if the $oid
1094
            // is obtained by a new entity because the old one went out of scope.
1095
            //$this->entityStates[$oid] = self::STATE_NEW;
1096 53
            if ( ! $class->isIdentifierNatural()) {
1097 43
                $class->reflFields[$class->identifier[0]]->setValue($entity, null);
1098
            }
1099
1100 53
            if ($invoke !== ListenersInvoker::INVOKE_NONE) {
1101 53
                $this->listenersInvoker->invoke($class, Events::postRemove, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
1102
            }
1103
        }
1104 52
    }
1105
1106
    /**
1107
     * Gets the commit order.
1108
     *
1109
     * @param array|null $entityChangeSet
1110
     *
1111
     * @return array
1112
     */
1113 988
    private function getCommitOrder(array $entityChangeSet = null)
1114
    {
1115 988
        if ($entityChangeSet === null) {
1116 988
            $entityChangeSet = array_merge($this->entityInsertions, $this->entityUpdates, $this->entityDeletions);
1117
        }
1118
1119 988
        $calc = $this->getCommitOrderCalculator();
1120
1121
        // See if there are any new classes in the changeset, that are not in the
1122
        // commit order graph yet (don't have a node).
1123
        // We have to inspect changeSet to be able to correctly build dependencies.
1124
        // It is not possible to use IdentityMap here because post inserted ids
1125
        // are not yet available.
1126 988
        $newNodes = [];
1127
1128 988
        foreach ($entityChangeSet as $entity) {
1129 988
            $class = $this->em->getClassMetadata(get_class($entity));
1130
1131 988
            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...
1132 620
                continue;
1133
            }
1134
1135 988
            $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...
1136
1137 988
            $newNodes[] = $class;
1138
        }
1139
1140
        // Calculate dependencies for new nodes
1141 988
        while ($class = array_pop($newNodes)) {
1142 988
            foreach ($class->associationMappings as $assoc) {
1143 873
                if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) {
1144 837
                    continue;
1145
                }
1146
1147 830
                $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
1148
1149 830
                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...
1150 642
                    $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...
1151
1152 642
                    $newNodes[] = $targetClass;
1153
                }
1154
1155 830
                $joinColumns = reset($assoc['joinColumns']);
1156
1157 830
                $calc->addDependency($targetClass->name, $class->name, (int)empty($joinColumns['nullable']));
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1158
1159
                // If the target class has mapped subclasses, these share the same dependency.
1160 830
                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...
1161 823
                    continue;
1162
                }
1163
1164 224
                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...
1165 224
                    $targetSubClass = $this->em->getClassMetadata($subClassName);
1166
1167 224
                    if ( ! $calc->hasNode($subClassName)) {
1168 195
                        $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...
1169
1170 195
                        $newNodes[] = $targetSubClass;
1171
                    }
1172
1173 224
                    $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...
1174
                }
1175
            }
1176
        }
1177
1178 988
        return $calc->sort();
1179
    }
1180
1181
    /**
1182
     * Schedules an entity for insertion into the database.
1183
     * If the entity already has an identifier, it will be added to the identity map.
1184
     *
1185
     * @param object $entity The entity to schedule for insertion.
1186
     *
1187
     * @return void
1188
     *
1189
     * @throws ORMInvalidArgumentException
1190
     * @throws \InvalidArgumentException
1191
     */
1192 1014
    public function scheduleForInsert($entity)
1193
    {
1194 1014
        $oid = spl_object_hash($entity);
1195
1196 1014
        if (isset($this->entityUpdates[$oid])) {
1197
            throw new InvalidArgumentException("Dirty entity can not be scheduled for insertion.");
1198
        }
1199
1200 1014
        if (isset($this->entityDeletions[$oid])) {
1201 1
            throw ORMInvalidArgumentException::scheduleInsertForRemovedEntity($entity);
1202
        }
1203 1014
        if (isset($this->originalEntityData[$oid]) && ! isset($this->entityInsertions[$oid])) {
1204 1
            throw ORMInvalidArgumentException::scheduleInsertForManagedEntity($entity);
1205
        }
1206
1207 1014
        if (isset($this->entityInsertions[$oid])) {
1208 1
            throw ORMInvalidArgumentException::scheduleInsertTwice($entity);
1209
        }
1210
1211 1014
        $this->entityInsertions[$oid] = $entity;
1212
1213 1014
        if (isset($this->entityIdentifiers[$oid])) {
1214 262
            $this->addToIdentityMap($entity);
1215
        }
1216
1217 1014
        if ($entity instanceof NotifyPropertyChanged) {
1218 5
            $entity->addPropertyChangedListener($this);
1219
        }
1220 1014
    }
1221
1222
    /**
1223
     * Checks whether an entity is scheduled for insertion.
1224
     *
1225
     * @param object $entity
1226
     *
1227
     * @return boolean
1228
     */
1229 627
    public function isScheduledForInsert($entity)
1230
    {
1231 627
        return isset($this->entityInsertions[spl_object_hash($entity)]);
1232
    }
1233
1234
    /**
1235
     * Schedules an entity for being updated.
1236
     *
1237
     * @param object $entity The entity to schedule for being updated.
1238
     *
1239
     * @return void
1240
     *
1241
     * @throws ORMInvalidArgumentException
1242
     */
1243
    public function scheduleForUpdate($entity)
1244
    {
1245
        $oid = spl_object_hash($entity);
1246
1247
        if ( ! isset($this->entityIdentifiers[$oid])) {
1248
            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "scheduling for update");
1249
        }
1250
1251
        if (isset($this->entityDeletions[$oid])) {
1252
            throw ORMInvalidArgumentException::entityIsRemoved($entity, "schedule for update");
1253
        }
1254
1255
        if ( ! isset($this->entityUpdates[$oid]) && ! isset($this->entityInsertions[$oid])) {
1256
            $this->entityUpdates[$oid] = $entity;
1257
        }
1258
    }
1259
1260
    /**
1261
     * INTERNAL:
1262
     * Schedules an extra update that will be executed immediately after the
1263
     * regular entity updates within the currently running commit cycle.
1264
     *
1265
     * Extra updates for entities are stored as (entity, changeset) tuples.
1266
     *
1267
     * @ignore
1268
     *
1269
     * @param object $entity    The entity for which to schedule an extra update.
1270
     * @param array  $changeset The changeset of the entity (what to update).
1271
     *
1272
     * @return void
1273
     */
1274 43
    public function scheduleExtraUpdate($entity, array $changeset)
1275
    {
1276 43
        $oid         = spl_object_hash($entity);
1277 43
        $extraUpdate = [$entity, $changeset];
1278
1279 43
        if (isset($this->extraUpdates[$oid])) {
1280 1
            list(, $changeset2) = $this->extraUpdates[$oid];
1281
1282 1
            $extraUpdate = [$entity, $changeset + $changeset2];
1283
        }
1284
1285 43
        $this->extraUpdates[$oid] = $extraUpdate;
1286 43
    }
1287
1288
    /**
1289
     * Checks whether an entity is registered as dirty in the unit of work.
1290
     * Note: Is not very useful currently as dirty entities are only registered
1291
     * at commit time.
1292
     *
1293
     * @param object $entity
1294
     *
1295
     * @return boolean
1296
     */
1297
    public function isScheduledForUpdate($entity)
1298
    {
1299
        return isset($this->entityUpdates[spl_object_hash($entity)]);
1300
    }
1301
1302
    /**
1303
     * Checks whether an entity is registered to be checked in the unit of work.
1304
     *
1305
     * @param object $entity
1306
     *
1307
     * @return boolean
1308
     */
1309 1
    public function isScheduledForDirtyCheck($entity)
1310
    {
1311 1
        $rootEntityName = $this->em->getClassMetadata(get_class($entity))->rootEntityName;
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1312
1313 1
        return isset($this->scheduledForSynchronization[$rootEntityName][spl_object_hash($entity)]);
1314
    }
1315
1316
    /**
1317
     * INTERNAL:
1318
     * Schedules an entity for deletion.
1319
     *
1320
     * @param object $entity
1321
     *
1322
     * @return void
1323
     */
1324 56
    public function scheduleForDelete($entity)
1325
    {
1326 56
        $oid = spl_object_hash($entity);
1327
1328 56
        if (isset($this->entityInsertions[$oid])) {
1329 1
            if ($this->isInIdentityMap($entity)) {
1330
                $this->removeFromIdentityMap($entity);
1331
            }
1332
1333 1
            unset($this->entityInsertions[$oid], $this->entityStates[$oid]);
1334
1335 1
            return; // entity has not been persisted yet, so nothing more to do.
1336
        }
1337
1338 56
        if ( ! $this->isInIdentityMap($entity)) {
1339 1
            return;
1340
        }
1341
1342 55
        $this->removeFromIdentityMap($entity);
1343
1344 55
        unset($this->entityUpdates[$oid]);
1345
1346 55
        if ( ! isset($this->entityDeletions[$oid])) {
1347 55
            $this->entityDeletions[$oid] = $entity;
1348 55
            $this->entityStates[$oid]    = self::STATE_REMOVED;
1349
        }
1350 55
    }
1351
1352
    /**
1353
     * Checks whether an entity is registered as removed/deleted with the unit
1354
     * of work.
1355
     *
1356
     * @param object $entity
1357
     *
1358
     * @return boolean
1359
     */
1360 16
    public function isScheduledForDelete($entity)
1361
    {
1362 16
        return isset($this->entityDeletions[spl_object_hash($entity)]);
1363
    }
1364
1365
    /**
1366
     * Checks whether an entity is scheduled for insertion, update or deletion.
1367
     *
1368
     * @param object $entity
1369
     *
1370
     * @return boolean
1371
     */
1372
    public function isEntityScheduled($entity)
1373
    {
1374
        $oid = spl_object_hash($entity);
1375
1376
        return isset($this->entityInsertions[$oid])
1377
            || isset($this->entityUpdates[$oid])
1378
            || isset($this->entityDeletions[$oid]);
1379
    }
1380
1381
    /**
1382
     * INTERNAL:
1383
     * Registers an entity in the identity map.
1384
     * Note that entities in a hierarchy are registered with the class name of
1385
     * the root entity.
1386
     *
1387
     * @ignore
1388
     *
1389
     * @param object $entity The entity to register.
1390
     *
1391
     * @return boolean TRUE if the registration was successful, FALSE if the identity of
1392
     *                 the entity in question is already managed.
1393
     *
1394
     * @throws ORMInvalidArgumentException
1395
     */
1396 1082
    public function addToIdentityMap($entity)
1397
    {
1398 1082
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1399 1082
        $identifier    = $this->entityIdentifiers[spl_object_hash($entity)];
1400
1401 1082
        if (empty($identifier) || in_array(null, $identifier, true)) {
1402 6
            throw ORMInvalidArgumentException::entityWithoutIdentity($classMetadata->name, $entity);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1403
        }
1404
1405 1076
        $idHash    = implode(' ', $identifier);
1406 1076
        $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...
1407
1408 1076
        if (isset($this->identityMap[$className][$idHash])) {
1409 82
            return false;
1410
        }
1411
1412 1076
        $this->identityMap[$className][$idHash] = $entity;
1413
1414 1076
        return true;
1415
    }
1416
1417
    /**
1418
     * Gets the state of an entity with regard to the current unit of work.
1419
     *
1420
     * @param object   $entity
1421
     * @param int|null $assume The state to assume if the state is not yet known (not MANAGED or REMOVED).
1422
     *                         This parameter can be set to improve performance of entity state detection
1423
     *                         by potentially avoiding a database lookup if the distinction between NEW and DETACHED
1424
     *                         is either known or does not matter for the caller of the method.
1425
     *
1426
     * @return int The entity state.
1427
     */
1428 1026
    public function getEntityState($entity, $assume = null)
1429
    {
1430 1026
        $oid = spl_object_hash($entity);
1431
1432 1026
        if (isset($this->entityStates[$oid])) {
1433 768
            return $this->entityStates[$oid];
1434
        }
1435
1436 1020
        if ($assume !== null) {
1437 1017
            return $assume;
1438
        }
1439
1440
        // State can only be NEW or DETACHED, because MANAGED/REMOVED states are known.
1441
        // Note that you can not remember the NEW or DETACHED state in _entityStates since
1442
        // the UoW does not hold references to such objects and the object hash can be reused.
1443
        // More generally because the state may "change" between NEW/DETACHED without the UoW being aware of it.
1444 12
        $class = $this->em->getClassMetadata(get_class($entity));
1445 12
        $id    = $class->getIdentifierValues($entity);
1446
1447 12
        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...
1448 5
            return self::STATE_NEW;
1449
        }
1450
1451 9
        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...
1452 1
            $id = $this->identifierFlattener->flattenIdentifier($class, $id);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

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

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

Loading history...
1453
        }
1454
1455
        switch (true) {
1456 9
            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...
1457
                // Check for a version field, if available, to avoid a db lookup.
1458 4
                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...
1459 1
                    return ($class->getFieldValue($entity, $class->versionField))
0 ignored issues
show
Bug introduced by
Accessing versionField on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1460
                        ? self::STATE_DETACHED
1461 1
                        : self::STATE_NEW;
1462
                }
1463
1464
                // Last try before db lookup: check the identity map.
1465 3
                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...
1466 1
                    return self::STATE_DETACHED;
1467
                }
1468
1469
                // db lookup
1470 3
                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...
1471
                    return self::STATE_DETACHED;
1472
                }
1473
1474 3
                return self::STATE_NEW;
1475
1476 5
            case ( ! $class->idGenerator->isPostInsertGenerator()):
0 ignored issues
show
Bug introduced by
Accessing idGenerator on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1477
                // if we have a pre insert generator we can't be sure that having an id
1478
                // really means that the entity exists. We have to verify this through
1479
                // the last resort: a db lookup
1480
1481
                // Last try before db lookup: check the identity map.
1482
                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...
1483
                    return self::STATE_DETACHED;
1484
                }
1485
1486
                // db lookup
1487
                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...
1488
                    return self::STATE_DETACHED;
1489
                }
1490
1491
                return self::STATE_NEW;
1492
1493
            default:
1494 5
                return self::STATE_DETACHED;
1495
        }
1496
    }
1497
1498
    /**
1499
     * INTERNAL:
1500
     * Removes an entity from the identity map. This effectively detaches the
1501
     * entity from the persistence management of Doctrine.
1502
     *
1503
     * @ignore
1504
     *
1505
     * @param object $entity
1506
     *
1507
     * @return boolean
1508
     *
1509
     * @throws ORMInvalidArgumentException
1510
     */
1511 67
    public function removeFromIdentityMap($entity)
1512
    {
1513 67
        $oid           = spl_object_hash($entity);
1514 67
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1515 67
        $idHash        = implode(' ', $this->entityIdentifiers[$oid]);
1516
1517 67
        if ($idHash === '') {
1518
            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "remove from identity map");
1519
        }
1520
1521 67
        $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...
1522
1523 67
        if (isset($this->identityMap[$className][$idHash])) {
1524 67
            unset($this->identityMap[$className][$idHash]);
1525 67
            unset($this->readOnlyObjects[$oid]);
1526
1527
            //$this->entityStates[$oid] = self::STATE_DETACHED;
1528
1529 67
            return true;
1530
        }
1531
1532
        return false;
1533
    }
1534
1535
    /**
1536
     * INTERNAL:
1537
     * Gets an entity in the identity map by its identifier hash.
1538
     *
1539
     * @ignore
1540
     *
1541
     * @param string $idHash
1542
     * @param string $rootClassName
1543
     *
1544
     * @return object
1545
     */
1546 6
    public function getByIdHash($idHash, $rootClassName)
1547
    {
1548 6
        return $this->identityMap[$rootClassName][$idHash];
1549
    }
1550
1551
    /**
1552
     * INTERNAL:
1553
     * Tries to get an entity by its identifier hash. If no entity is found for
1554
     * the given hash, FALSE is returned.
1555
     *
1556
     * @ignore
1557
     *
1558
     * @param mixed  $idHash        (must be possible to cast it to string)
1559
     * @param string $rootClassName
1560
     *
1561
     * @return object|bool The found entity or FALSE.
1562
     */
1563 34
    public function tryGetByIdHash($idHash, $rootClassName)
1564
    {
1565 34
        $stringIdHash = (string) $idHash;
1566
1567 34
        return isset($this->identityMap[$rootClassName][$stringIdHash])
1568 34
            ? $this->identityMap[$rootClassName][$stringIdHash]
1569 34
            : false;
1570
    }
1571
1572
    /**
1573
     * Checks whether an entity is registered in the identity map of this UnitOfWork.
1574
     *
1575
     * @param object $entity
1576
     *
1577
     * @return boolean
1578
     */
1579 198
    public function isInIdentityMap($entity)
1580
    {
1581 198
        $oid = spl_object_hash($entity);
1582
1583 198
        if (empty($this->entityIdentifiers[$oid])) {
1584 32
            return false;
1585
        }
1586
1587 182
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1588 182
        $idHash        = implode(' ', $this->entityIdentifiers[$oid]);
1589
1590 182
        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...
1591
    }
1592
1593
    /**
1594
     * INTERNAL:
1595
     * Checks whether an identifier hash exists in the identity map.
1596
     *
1597
     * @ignore
1598
     *
1599
     * @param string $idHash
1600
     * @param string $rootClassName
1601
     *
1602
     * @return boolean
1603
     */
1604
    public function containsIdHash($idHash, $rootClassName)
1605
    {
1606
        return isset($this->identityMap[$rootClassName][$idHash]);
1607
    }
1608
1609
    /**
1610
     * Persists an entity as part of the current unit of work.
1611
     *
1612
     * @param object $entity The entity to persist.
1613
     *
1614
     * @return void
1615
     */
1616 1009
    public function persist($entity)
1617
    {
1618 1009
        $visited = [];
1619
1620 1009
        $this->doPersist($entity, $visited);
1621 1002
    }
1622
1623
    /**
1624
     * Persists an entity as part of the current unit of work.
1625
     *
1626
     * This method is internally called during persist() cascades as it tracks
1627
     * the already visited entities to prevent infinite recursions.
1628
     *
1629
     * @param object $entity  The entity to persist.
1630
     * @param array  $visited The already visited entities.
1631
     *
1632
     * @return void
1633
     *
1634
     * @throws ORMInvalidArgumentException
1635
     * @throws UnexpectedValueException
1636
     */
1637 1009
    private function doPersist($entity, array &$visited)
1638
    {
1639 1009
        $oid = spl_object_hash($entity);
1640
1641 1009
        if (isset($visited[$oid])) {
1642 111
            return; // Prevent infinite recursion
1643
        }
1644
1645 1009
        $visited[$oid] = $entity; // Mark visited
1646
1647 1009
        $class = $this->em->getClassMetadata(get_class($entity));
1648
1649
        // We assume NEW, so DETACHED entities result in an exception on flush (constraint violation).
1650
        // If we would detect DETACHED here we would throw an exception anyway with the same
1651
        // consequences (not recoverable/programming error), so just assuming NEW here
1652
        // lets us avoid some database lookups for entities with natural identifiers.
1653 1009
        $entityState = $this->getEntityState($entity, self::STATE_NEW);
1654
1655
        switch ($entityState) {
1656 1009
            case self::STATE_MANAGED:
1657
                // Nothing to do, except if policy is "deferred explicit"
1658 228
                if ($class->isChangeTrackingDeferredExplicit()) {
1659 2
                    $this->scheduleForDirtyCheck($entity);
1660
                }
1661 228
                break;
1662
1663 1009
            case self::STATE_NEW:
1664 1008
                $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...
1665 1008
                break;
1666
1667 1
            case self::STATE_REMOVED:
1668
                // Entity becomes managed again
1669 1
                unset($this->entityDeletions[$oid]);
1670 1
                $this->addToIdentityMap($entity);
1671
1672 1
                $this->entityStates[$oid] = self::STATE_MANAGED;
1673 1
                break;
1674
1675
            case self::STATE_DETACHED:
1676
                // Can actually not happen right now since we assume STATE_NEW.
1677
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "persisted");
1678
1679
            default:
1680
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1681
        }
1682
1683 1009
        $this->cascadePersist($entity, $visited);
1684 1002
    }
1685
1686
    /**
1687
     * Deletes an entity as part of the current unit of work.
1688
     *
1689
     * @param object $entity The entity to remove.
1690
     *
1691
     * @return void
1692
     */
1693 55
    public function remove($entity)
1694
    {
1695 55
        $visited = [];
1696
1697 55
        $this->doRemove($entity, $visited);
1698 55
    }
1699
1700
    /**
1701
     * Deletes an entity as part of the current unit of work.
1702
     *
1703
     * This method is internally called during delete() cascades as it tracks
1704
     * the already visited entities to prevent infinite recursions.
1705
     *
1706
     * @param object $entity  The entity to delete.
1707
     * @param array  $visited The map of the already visited entities.
1708
     *
1709
     * @return void
1710
     *
1711
     * @throws ORMInvalidArgumentException If the instance is a detached entity.
1712
     * @throws UnexpectedValueException
1713
     */
1714 55
    private function doRemove($entity, array &$visited)
1715
    {
1716 55
        $oid = spl_object_hash($entity);
1717
1718 55
        if (isset($visited[$oid])) {
1719 1
            return; // Prevent infinite recursion
1720
        }
1721
1722 55
        $visited[$oid] = $entity; // mark visited
1723
1724
        // Cascade first, because scheduleForDelete() removes the entity from the identity map, which
1725
        // can cause problems when a lazy proxy has to be initialized for the cascade operation.
1726 55
        $this->cascadeRemove($entity, $visited);
1727
1728 55
        $class       = $this->em->getClassMetadata(get_class($entity));
1729 55
        $entityState = $this->getEntityState($entity);
1730
1731
        switch ($entityState) {
1732 55
            case self::STATE_NEW:
1733 55
            case self::STATE_REMOVED:
1734
                // nothing to do
1735 1
                break;
1736
1737 55
            case self::STATE_MANAGED:
1738 55
                $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...
1739
1740 55
                if ($invoke !== ListenersInvoker::INVOKE_NONE) {
1741 7
                    $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...
1742
                }
1743
1744 55
                $this->scheduleForDelete($entity);
1745 55
                break;
1746
1747
            case self::STATE_DETACHED:
1748
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "removed");
1749
            default:
1750
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1751
        }
1752
1753 55
    }
1754
1755
    /**
1756
     * Merges the state of the given detached entity into this UnitOfWork.
1757
     *
1758
     * @param object $entity
1759
     *
1760
     * @return object The managed copy of the entity.
1761
     *
1762
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1763
     *         attribute and the version check against the managed copy fails.
1764
     *
1765
     * @todo Require active transaction!? OptimisticLockException may result in undefined state!?
1766
     */
1767 41
    public function merge($entity)
1768
    {
1769 41
        $visited = [];
1770
1771 41
        return $this->doMerge($entity, $visited);
1772
    }
1773
1774
    /**
1775
     * Executes a merge operation on an entity.
1776
     *
1777
     * @param object      $entity
1778
     * @param array       $visited
1779
     * @param object|null $prevManagedCopy
1780
     * @param array|null  $assoc
1781
     *
1782
     * @return object The managed copy of the entity.
1783
     *
1784
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1785
     *         attribute and the version check against the managed copy fails.
1786
     * @throws ORMInvalidArgumentException If the entity instance is NEW.
1787
     * @throws EntityNotFoundException if an assigned identifier is used in the entity, but none is provided
1788
     */
1789 41
    private function doMerge($entity, array &$visited, $prevManagedCopy = null, array $assoc = [])
1790
    {
1791 41
        $oid = spl_object_hash($entity);
1792
1793 41
        if (isset($visited[$oid])) {
1794 3
            $managedCopy = $visited[$oid];
1795
1796 3
            if ($prevManagedCopy !== null) {
1797 3
                $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy);
1798
            }
1799
1800 3
            return $managedCopy;
1801
        }
1802
1803 41
        $class = $this->em->getClassMetadata(get_class($entity));
1804
1805
        // First we assume DETACHED, although it can still be NEW but we can avoid
1806
        // an extra db-roundtrip this way. If it is not MANAGED but has an identity,
1807
        // we need to fetch it from the db anyway in order to merge.
1808
        // MANAGED entities are ignored by the merge operation.
1809 41
        $managedCopy = $entity;
1810
1811 41
        if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) {
1812
            // Try to look the entity up in the identity map.
1813 40
            $id = $class->getIdentifierValues($entity);
1814
1815
            // If there is no ID, it is actually NEW.
1816 40
            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...
1817 6
                $managedCopy = $this->newInstance($class);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

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

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

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

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

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

Loading history...
1821
            } else {
1822 35
                $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...
1823 3
                    ? $this->identifierFlattener->flattenIdentifier($class, $id)
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

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

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

Loading history...
1824 35
                    : $id;
1825
1826 35
                $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 1875 which is incompatible with the return type documented by Doctrine\ORM\UnitOfWork::doMerge of type object.
Loading history...
1827
1828 35
                if ($managedCopy) {
1829
                    // We have the entity in-memory already, just make sure its not removed.
1830 14
                    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 1826 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...
1831 14
                        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 1826 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...
1832
                    }
1833
                } else {
1834
                    // We need to fetch the managed copy in order to merge.
1835 23
                    $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...
1836
                }
1837
1838 35
                if ($managedCopy === null) {
1839
                    // If the identifier is ASSIGNED, it is NEW, otherwise an error
1840
                    // since the managed entity was not found.
1841 2
                    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...
1842
                        throw EntityNotFoundException::fromClassNameAndIdentifier(
1843
                            $class->getName(),
1844
                            $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...
1845
                        );
1846
                    }
1847
1848 2
                    $managedCopy = $this->newInstance($class);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

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

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

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

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

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

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

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

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

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

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

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

    return array();
}

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

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

Loading history...
1855 32
                    $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy);
0 ignored issues
show
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1826 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...
1856
                }
1857
            }
1858
1859 39
            $visited[$oid] = $managedCopy; // mark visited
1860
1861 39
            if ($class->isChangeTrackingDeferredExplicit()) {
1862
                $this->scheduleForDirtyCheck($entity);
1863
            }
1864
        }
1865
1866 40
        if ($prevManagedCopy !== null) {
1867 5
            $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy);
0 ignored issues
show
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1826 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...
1868
        }
1869
1870
        // Mark the managed copy visited as well
1871 40
        $visited[spl_object_hash($managedCopy)] = $managedCopy;
1872
1873 40
        $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 1826 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...
1874
1875 40
        return $managedCopy;
1876
    }
1877
1878
    /**
1879
     * @param ClassMetadata $class
1880
     * @param object        $entity
1881
     * @param object        $managedCopy
1882
     *
1883
     * @return void
1884
     *
1885
     * @throws OptimisticLockException
1886
     */
1887 33
    private function ensureVersionMatch(ClassMetadata $class, $entity, $managedCopy)
1888
    {
1889 33
        if (! ($class->isVersioned && $this->isLoaded($managedCopy) && $this->isLoaded($entity))) {
1890 30
            return;
1891
        }
1892
1893 4
        $reflField          = $class->reflFields[$class->versionField];
1894 4
        $managedCopyVersion = $reflField->getValue($managedCopy);
1895 4
        $entityVersion      = $reflField->getValue($entity);
1896
1897
        // Throw exception if versions don't match.
1898 4
        if ($managedCopyVersion == $entityVersion) {
1899 3
            return;
1900
        }
1901
1902 1
        throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion);
1903
    }
1904
1905
    /**
1906
     * Tests if an entity is loaded - must either be a loaded proxy or not a proxy
1907
     *
1908
     * @param object $entity
1909
     *
1910
     * @return bool
1911
     */
1912 40
    private function isLoaded($entity)
1913
    {
1914 40
        return !($entity instanceof Proxy) || $entity->__isInitialized();
1915
    }
1916
1917
    /**
1918
     * Sets/adds associated managed copies into the previous entity's association field
1919
     *
1920
     * @param object $entity
1921
     * @param array  $association
1922
     * @param object $previousManagedCopy
1923
     * @param object $managedCopy
1924
     *
1925
     * @return void
1926
     */
1927 5
    private function updateAssociationWithMergedEntity($entity, array $association, $previousManagedCopy, $managedCopy)
1928
    {
1929 5
        $assocField = $association['fieldName'];
1930 5
        $prevClass  = $this->em->getClassMetadata(get_class($previousManagedCopy));
1931
1932 5
        if ($association['type'] & ClassMetadata::TO_ONE) {
1933 5
            $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...
1934
1935 5
            return;
1936
        }
1937
1938
        $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...
1939
        $value[] = $managedCopy;
1940
1941
        if ($association['type'] == ClassMetadata::ONE_TO_MANY) {
1942
            $class = $this->em->getClassMetadata(get_class($entity));
1943
1944
            $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...
1945
        }
1946
    }
1947
1948
    /**
1949
     * Detaches an entity from the persistence management. It's persistence will
1950
     * no longer be managed by Doctrine.
1951
     *
1952
     * @param object $entity The entity to detach.
1953
     *
1954
     * @return void
1955
     */
1956 11
    public function detach($entity)
1957
    {
1958 11
        $visited = [];
1959
1960 11
        $this->doDetach($entity, $visited);
1961 11
    }
1962
1963
    /**
1964
     * Executes a detach operation on the given entity.
1965
     *
1966
     * @param object  $entity
1967
     * @param array   $visited
1968
     * @param boolean $noCascade if true, don't cascade detach operation.
1969
     *
1970
     * @return void
1971
     */
1972 15
    private function doDetach($entity, array &$visited, $noCascade = false)
1973
    {
1974 15
        $oid = spl_object_hash($entity);
1975
1976 15
        if (isset($visited[$oid])) {
1977
            return; // Prevent infinite recursion
1978
        }
1979
1980 15
        $visited[$oid] = $entity; // mark visited
1981
1982 15
        switch ($this->getEntityState($entity, self::STATE_DETACHED)) {
1983 15
            case self::STATE_MANAGED:
1984 13
                if ($this->isInIdentityMap($entity)) {
1985 12
                    $this->removeFromIdentityMap($entity);
1986
                }
1987
1988
                unset(
1989 13
                    $this->entityInsertions[$oid],
1990 13
                    $this->entityUpdates[$oid],
1991 13
                    $this->entityDeletions[$oid],
1992 13
                    $this->entityIdentifiers[$oid],
1993 13
                    $this->entityStates[$oid],
1994 13
                    $this->originalEntityData[$oid]
1995
                );
1996 13
                break;
1997 3
            case self::STATE_NEW:
1998 3
            case self::STATE_DETACHED:
1999 3
                return;
2000
        }
2001
2002 13
        if ( ! $noCascade) {
2003 13
            $this->cascadeDetach($entity, $visited);
2004
        }
2005 13
    }
2006
2007
    /**
2008
     * Refreshes the state of the given entity from the database, overwriting
2009
     * any local, unpersisted changes.
2010
     *
2011
     * @param object $entity The entity to refresh.
2012
     *
2013
     * @return void
2014
     *
2015
     * @throws InvalidArgumentException If the entity is not MANAGED.
2016
     */
2017 15
    public function refresh($entity)
2018
    {
2019 15
        $visited = [];
2020
2021 15
        $this->doRefresh($entity, $visited);
2022 15
    }
2023
2024
    /**
2025
     * Executes a refresh operation on an entity.
2026
     *
2027
     * @param object $entity  The entity to refresh.
2028
     * @param array  $visited The already visited entities during cascades.
2029
     *
2030
     * @return void
2031
     *
2032
     * @throws ORMInvalidArgumentException If the entity is not MANAGED.
2033
     */
2034 15
    private function doRefresh($entity, array &$visited)
2035
    {
2036 15
        $oid = spl_object_hash($entity);
2037
2038 15
        if (isset($visited[$oid])) {
2039
            return; // Prevent infinite recursion
2040
        }
2041
2042 15
        $visited[$oid] = $entity; // mark visited
2043
2044 15
        $class = $this->em->getClassMetadata(get_class($entity));
2045
2046 15
        if ($this->getEntityState($entity) !== self::STATE_MANAGED) {
2047
            throw ORMInvalidArgumentException::entityNotManaged($entity);
2048
        }
2049
2050 15
        $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...
2051 15
            array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
2052 15
            $entity
2053
        );
2054
2055 15
        $this->cascadeRefresh($entity, $visited);
2056 15
    }
2057
2058
    /**
2059
     * Cascades a refresh operation to associated entities.
2060
     *
2061
     * @param object $entity
2062
     * @param array  $visited
2063
     *
2064
     * @return void
2065
     */
2066 15
    private function cascadeRefresh($entity, array &$visited)
2067
    {
2068 15
        $class = $this->em->getClassMetadata(get_class($entity));
2069
2070 15
        $associationMappings = array_filter(
2071 15
            $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...
2072
            function ($assoc) { return $assoc['isCascadeRefresh']; }
2073
        );
2074
2075 15
        foreach ($associationMappings as $assoc) {
2076 4
            $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...
2077
2078
            switch (true) {
2079 4
                case ($relatedEntities instanceof PersistentCollection):
2080
                    // Unwrap so that foreach() does not initialize
2081 4
                    $relatedEntities = $relatedEntities->unwrap();
2082
                    // break; is commented intentionally!
2083
2084
                case ($relatedEntities instanceof Collection):
2085
                case (is_array($relatedEntities)):
2086 4
                    foreach ($relatedEntities as $relatedEntity) {
2087
                        $this->doRefresh($relatedEntity, $visited);
2088
                    }
2089 4
                    break;
2090
2091
                case ($relatedEntities !== null):
2092
                    $this->doRefresh($relatedEntities, $visited);
2093
                    break;
2094
2095 4
                default:
2096
                    // Do nothing
2097
            }
2098
        }
2099 15
    }
2100
2101
    /**
2102
     * Cascades a detach operation to associated entities.
2103
     *
2104
     * @param object $entity
2105
     * @param array  $visited
2106
     *
2107
     * @return void
2108
     */
2109 13
    private function cascadeDetach($entity, array &$visited)
2110
    {
2111 13
        $class = $this->em->getClassMetadata(get_class($entity));
2112
2113 13
        $associationMappings = array_filter(
2114 13
            $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...
2115
            function ($assoc) { return $assoc['isCascadeDetach']; }
2116
        );
2117
2118 13
        foreach ($associationMappings as $assoc) {
2119 3
            $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2120
2121
            switch (true) {
2122 3
                case ($relatedEntities instanceof PersistentCollection):
2123
                    // Unwrap so that foreach() does not initialize
2124 2
                    $relatedEntities = $relatedEntities->unwrap();
2125
                    // break; is commented intentionally!
2126
2127 1
                case ($relatedEntities instanceof Collection):
2128
                case (is_array($relatedEntities)):
2129 3
                    foreach ($relatedEntities as $relatedEntity) {
2130 1
                        $this->doDetach($relatedEntity, $visited);
2131
                    }
2132 3
                    break;
2133
2134
                case ($relatedEntities !== null):
2135
                    $this->doDetach($relatedEntities, $visited);
2136
                    break;
2137
2138 3
                default:
2139
                    // Do nothing
2140
            }
2141
        }
2142 13
    }
2143
2144
    /**
2145
     * Cascades a merge operation to associated entities.
2146
     *
2147
     * @param object $entity
2148
     * @param object $managedCopy
2149
     * @param array  $visited
2150
     *
2151
     * @return void
2152
     */
2153 40
    private function cascadeMerge($entity, $managedCopy, array &$visited)
2154
    {
2155 40
        $class = $this->em->getClassMetadata(get_class($entity));
2156
2157 40
        $associationMappings = array_filter(
2158 40
            $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...
2159
            function ($assoc) { return $assoc['isCascadeMerge']; }
2160
        );
2161
2162 40
        foreach ($associationMappings as $assoc) {
2163 15
            $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...
2164
2165 15
            if ($relatedEntities instanceof Collection) {
2166 9
                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...
2167 1
                    continue;
2168
                }
2169
2170 8
                if ($relatedEntities instanceof PersistentCollection) {
2171
                    // Unwrap so that foreach() does not initialize
2172 4
                    $relatedEntities = $relatedEntities->unwrap();
2173
                }
2174
2175 8
                foreach ($relatedEntities as $relatedEntity) {
2176 8
                    $this->doMerge($relatedEntity, $visited, $managedCopy, $assoc);
2177
                }
2178 6
            } else if ($relatedEntities !== null) {
2179 14
                $this->doMerge($relatedEntities, $visited, $managedCopy, $assoc);
2180
            }
2181
        }
2182 40
    }
2183
2184
    /**
2185
     * Cascades the save operation to associated entities.
2186
     *
2187
     * @param object $entity
2188
     * @param array  $visited
2189
     *
2190
     * @return void
2191
     */
2192 1009
    private function cascadePersist($entity, array &$visited)
2193
    {
2194 1009
        $class = $this->em->getClassMetadata(get_class($entity));
2195
2196 1009
        $associationMappings = array_filter(
2197 1009
            $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...
2198
            function ($assoc) { return $assoc['isCascadePersist']; }
2199
        );
2200
2201 1009
        foreach ($associationMappings as $assoc) {
2202 642
            $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...
2203
2204
            switch (true) {
2205 642
                case ($relatedEntities instanceof PersistentCollection):
2206
                    // Unwrap so that foreach() does not initialize
2207 19
                    $relatedEntities = $relatedEntities->unwrap();
2208
                    // break; is commented intentionally!
2209
2210 642
                case ($relatedEntities instanceof Collection):
2211 588
                case (is_array($relatedEntities)):
2212 549
                    if (($assoc['type'] & ClassMetadata::TO_MANY) <= 0) {
2213 3
                        throw ORMInvalidArgumentException::invalidAssociation(
2214 3
                            $this->em->getClassMetadata($assoc['targetEntity']),
0 ignored issues
show
Compatibility introduced by
$this->em->getClassMetad...$assoc['targetEntity']) of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

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

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

Loading history...
2215
                            $assoc,
2216 3
                            $relatedEntities
2217
                        );
2218
                    }
2219
2220 546
                    foreach ($relatedEntities as $relatedEntity) {
2221 277
                        $this->doPersist($relatedEntity, $visited);
2222
                    }
2223
2224 546
                    break;
2225
2226 579
                case ($relatedEntities !== null):
2227 248
                    if (! $relatedEntities instanceof $assoc['targetEntity']) {
2228 4
                        throw ORMInvalidArgumentException::invalidAssociation(
2229 4
                            $this->em->getClassMetadata($assoc['targetEntity']),
0 ignored issues
show
Compatibility introduced by
$this->em->getClassMetad...$assoc['targetEntity']) of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

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

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

Loading history...
2230
                            $assoc,
2231 4
                            $relatedEntities
2232
                        );
2233
                    }
2234
2235 244
                    $this->doPersist($relatedEntities, $visited);
2236 244
                    break;
2237
2238 636
                default:
2239
                    // Do nothing
2240
            }
2241
        }
2242 1002
    }
2243
2244
    /**
2245
     * Cascades the delete operation to associated entities.
2246
     *
2247
     * @param object $entity
2248
     * @param array  $visited
2249
     *
2250
     * @return void
2251
     */
2252 55
    private function cascadeRemove($entity, array &$visited)
2253
    {
2254 55
        $class = $this->em->getClassMetadata(get_class($entity));
2255
2256 55
        $associationMappings = array_filter(
2257 55
            $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...
2258
            function ($assoc) { return $assoc['isCascadeRemove']; }
2259
        );
2260
2261 55
        $entitiesToCascade = [];
2262
2263 55
        foreach ($associationMappings as $assoc) {
2264 20
            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...
2265 6
                $entity->__load();
2266
            }
2267
2268 20
            $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...
2269
2270
            switch (true) {
2271 20
                case ($relatedEntities instanceof Collection):
2272 17
                case (is_array($relatedEntities)):
2273
                    // If its a PersistentCollection initialization is intended! No unwrap!
2274 15
                    foreach ($relatedEntities as $relatedEntity) {
2275 6
                        $entitiesToCascade[] = $relatedEntity;
2276
                    }
2277 15
                    break;
2278
2279 17
                case ($relatedEntities !== null):
2280 5
                    $entitiesToCascade[] = $relatedEntities;
2281 5
                    break;
2282
2283 20
                default:
2284
                    // Do nothing
2285
            }
2286
        }
2287
2288 55
        foreach ($entitiesToCascade as $relatedEntity) {
2289 10
            $this->doRemove($relatedEntity, $visited);
2290
        }
2291 55
    }
2292
2293
    /**
2294
     * Acquire a lock on the given entity.
2295
     *
2296
     * @param object $entity
2297
     * @param int    $lockMode
2298
     * @param int    $lockVersion
2299
     *
2300
     * @return void
2301
     *
2302
     * @throws ORMInvalidArgumentException
2303
     * @throws TransactionRequiredException
2304
     * @throws OptimisticLockException
2305
     */
2306 8
    public function lock($entity, $lockMode, $lockVersion = null)
2307
    {
2308 8
        if ($entity === null) {
2309 1
            throw new \InvalidArgumentException("No entity passed to UnitOfWork#lock().");
2310
        }
2311
2312 7
        if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) {
2313 1
            throw ORMInvalidArgumentException::entityNotManaged($entity);
2314
        }
2315
2316 6
        $class = $this->em->getClassMetadata(get_class($entity));
2317
2318
        switch (true) {
2319 6
            case LockMode::OPTIMISTIC === $lockMode:
2320 4
                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...
2321 2
                    throw OptimisticLockException::notVersioned($class->name);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2322
                }
2323
2324 2
                if ($lockVersion === null) {
2325
                    return;
2326
                }
2327
2328 2
                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...
2329
                    $entity->__load();
2330
                }
2331
2332 2
                $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...
2333
2334 2
                if ($entityVersion != $lockVersion) {
2335 2
                    throw OptimisticLockException::lockFailedVersionMismatch($entity, $lockVersion, $entityVersion);
2336
                }
2337
2338
                break;
2339
2340 2
            case LockMode::NONE === $lockMode:
2341 2
            case LockMode::PESSIMISTIC_READ === $lockMode:
2342 1
            case LockMode::PESSIMISTIC_WRITE === $lockMode:
2343 2
                if (!$this->em->getConnection()->isTransactionActive()) {
2344 2
                    throw TransactionRequiredException::transactionRequired();
2345
                }
2346
2347
                $oid = spl_object_hash($entity);
2348
2349
                $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...
2350
                    array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
2351
                    $lockMode
2352
                );
2353
                break;
2354
2355
            default:
2356
                // Do nothing
2357
        }
2358
    }
2359
2360
    /**
2361
     * Gets the CommitOrderCalculator used by the UnitOfWork to order commits.
2362
     *
2363
     * @return \Doctrine\ORM\Internal\CommitOrderCalculator
2364
     */
2365 988
    public function getCommitOrderCalculator()
2366
    {
2367 988
        return new Internal\CommitOrderCalculator();
2368
    }
2369
2370
    /**
2371
     * Clears the UnitOfWork.
2372
     *
2373
     * @param string|null $entityName if given, only entities of this type will get detached.
2374
     *
2375
     * @return void
2376
     *
2377
     * @throws ORMInvalidArgumentException if an invalid entity name is given
2378
     */
2379 1199
    public function clear($entityName = null)
2380
    {
2381 1199
        if ($entityName === null) {
2382 1197
            $this->identityMap =
2383 1197
            $this->entityIdentifiers =
2384 1197
            $this->originalEntityData =
2385 1197
            $this->entityChangeSets =
2386 1197
            $this->entityStates =
2387 1197
            $this->scheduledForSynchronization =
2388 1197
            $this->entityInsertions =
2389 1197
            $this->entityUpdates =
2390 1197
            $this->entityDeletions =
2391 1197
            $this->collectionDeletions =
2392 1197
            $this->collectionUpdates =
2393 1197
            $this->extraUpdates =
2394 1197
            $this->readOnlyObjects =
2395 1197
            $this->visitedCollections =
2396 1197
            $this->orphanRemovals = [];
2397
        } else {
2398 4
            $this->clearIdentityMapForEntityName($entityName);
2399 4
            $this->clearEntityInsertionsForEntityName($entityName);
2400
        }
2401
2402 1199
        if ($this->evm->hasListeners(Events::onClear)) {
2403 7
            $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em, $entityName));
2404
        }
2405 1199
    }
2406
2407
    /**
2408
     * INTERNAL:
2409
     * Schedules an orphaned entity for removal. The remove() operation will be
2410
     * invoked on that entity at the beginning of the next commit of this
2411
     * UnitOfWork.
2412
     *
2413
     * @ignore
2414
     *
2415
     * @param object $entity
2416
     *
2417
     * @return void
2418
     */
2419 16
    public function scheduleOrphanRemoval($entity)
2420
    {
2421 16
        $this->orphanRemovals[spl_object_hash($entity)] = $entity;
2422 16
    }
2423
2424
    /**
2425
     * INTERNAL:
2426
     * Cancels a previously scheduled orphan removal.
2427
     *
2428
     * @ignore
2429
     *
2430
     * @param object $entity
2431
     *
2432
     * @return void
2433
     */
2434 105
    public function cancelOrphanRemoval($entity)
2435
    {
2436 105
        unset($this->orphanRemovals[spl_object_hash($entity)]);
2437 105
    }
2438
2439
    /**
2440
     * INTERNAL:
2441
     * Schedules a complete collection for removal when this UnitOfWork commits.
2442
     *
2443
     * @param PersistentCollection $coll
2444
     *
2445
     * @return void
2446
     */
2447 11
    public function scheduleCollectionDeletion(PersistentCollection $coll)
2448
    {
2449 11
        $coid = spl_object_hash($coll);
2450
2451
        // TODO: if $coll is already scheduled for recreation ... what to do?
2452
        // Just remove $coll from the scheduled recreations?
2453 11
        unset($this->collectionUpdates[$coid]);
2454
2455 11
        $this->collectionDeletions[$coid] = $coll;
2456 11
    }
2457
2458
    /**
2459
     * @param PersistentCollection $coll
2460
     *
2461
     * @return bool
2462
     */
2463
    public function isCollectionScheduledForDeletion(PersistentCollection $coll)
2464
    {
2465
        return isset($this->collectionDeletions[spl_object_hash($coll)]);
2466
    }
2467
2468
    /**
2469
     * @param ClassMetadata $class
2470
     *
2471
     * @return \Doctrine\Common\Persistence\ObjectManagerAware|object
2472
     */
2473 671
    private function newInstance($class)
2474
    {
2475 671
        $entity = $class->newInstance();
2476
2477 671
        if ($entity instanceof \Doctrine\Common\Persistence\ObjectManagerAware) {
2478 4
            $entity->injectObjectManager($this->em, $class);
2479
        }
2480
2481 671
        return $entity;
2482
    }
2483
2484
    /**
2485
     * INTERNAL:
2486
     * Creates an entity. Used for reconstitution of persistent entities.
2487
     *
2488
     * Internal note: Highly performance-sensitive method.
2489
     *
2490
     * @ignore
2491
     *
2492
     * @param string $className The name of the entity class.
2493
     * @param array  $data      The data for the entity.
2494
     * @param array  $hints     Any hints to account for during reconstitution/lookup of the entity.
2495
     *
2496
     * @return object The managed entity instance.
2497
     *
2498
     * @todo Rename: getOrCreateEntity
2499
     */
2500 804
    public function createEntity($className, array $data, &$hints = [])
2501
    {
2502 804
        $class = $this->em->getClassMetadata($className);
2503
        //$isReadOnly = isset($hints[Query::HINT_READ_ONLY]);
2504
2505 804
        $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...
2506 804
        $idHash = implode(' ', $id);
2507
2508 804
        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...
2509 307
            $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...
2510 307
            $oid = spl_object_hash($entity);
2511
2512
            if (
2513 307
                isset($hints[Query::HINT_REFRESH])
2514 307
                && isset($hints[Query::HINT_REFRESH_ENTITY])
2515 307
                && ($unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY]) !== $entity
2516 307
                && $unmanagedProxy instanceof Proxy
2517 307
                && $this->isIdentifierEquals($unmanagedProxy, $entity)
2518
            ) {
2519
                // DDC-1238 - we have a managed instance, but it isn't the provided one.
2520
                // Therefore we clear its identifier. Also, we must re-fetch metadata since the
2521
                // refreshed object may be anything
2522
2523 2
                foreach ($class->identifier as $fieldName) {
0 ignored issues
show
Bug introduced by
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2525
                }
2526
2527 2
                return $unmanagedProxy;
2528
            }
2529
2530 305
            if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
2531 21
                $entity->__setInitialized(true);
2532
2533 21
                $overrideLocalValues = true;
2534
2535 21
                if ($entity instanceof NotifyPropertyChanged) {
2536 21
                    $entity->addPropertyChangedListener($this);
2537
                }
2538
            } else {
2539 285
                $overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
2540
2541
                // If only a specific entity is set to refresh, check that it's the one
2542 285
                if (isset($hints[Query::HINT_REFRESH_ENTITY])) {
2543 70
                    $overrideLocalValues = $hints[Query::HINT_REFRESH_ENTITY] === $entity;
2544
                }
2545
            }
2546
2547 305
            if ($overrideLocalValues) {
2548
                // inject ObjectManager upon refresh.
2549 108
                if ($entity instanceof ObjectManagerAware) {
2550 3
                    $entity->injectObjectManager($this->em, $class);
2551
                }
2552
2553 305
                $this->originalEntityData[$oid] = $data;
2554
            }
2555
        } else {
2556 666
            $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...
2557 666
            $oid    = spl_object_hash($entity);
2558
2559 666
            $this->entityIdentifiers[$oid]  = $id;
2560 666
            $this->entityStates[$oid]       = self::STATE_MANAGED;
2561 666
            $this->originalEntityData[$oid] = $data;
2562
2563 666
            $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...
2564
2565 666
            if ($entity instanceof NotifyPropertyChanged) {
2566 2
                $entity->addPropertyChangedListener($this);
2567
            }
2568
2569 666
            $overrideLocalValues = true;
2570
        }
2571
2572 803
        if ( ! $overrideLocalValues) {
2573 217
            return $entity;
2574
        }
2575
2576 700
        foreach ($data as $field => $value) {
2577 700
            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...
2578 700
                $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...
2579
            }
2580
        }
2581
2582
        // Loading the entity right here, if its in the eager loading map get rid of it there.
2583 700
        unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
2584
2585 700
        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...
2586
            unset($this->eagerLoadingEntities[$class->rootEntityName]);
2587
        }
2588
2589
        // Properly initialize any unfetched associations, if partial objects are not allowed.
2590 700
        if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
2591 34
            return $entity;
2592
        }
2593
2594 666
        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...
2595
            // Check if the association is not among the fetch-joined associations already.
2596 581
            if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) {
2597 245
                continue;
2598
            }
2599
2600 559
            $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
2601
2602
            switch (true) {
2603 559
                case ($assoc['type'] & ClassMetadata::TO_ONE):
2604 483
                    if ( ! $assoc['isOwningSide']) {
2605
2606
                        // use the given entity association
2607 66
                        if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2608
2609 3
                            $this->originalEntityData[$oid][$field] = $data[$field];
2610
2611 3
                            $class->reflFields[$field]->setValue($entity, $data[$field]);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2612 3
                            $targetClass->reflFields[$assoc['mappedBy']]->setValue($data[$field], $entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2613
2614 3
                            continue 2;
2615
                        }
2616
2617
                        // Inverse side of x-to-one can never be lazy
2618 63
                        $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...
2619
2620 63
                        continue 2;
2621
                    }
2622
2623
                    // use the entity association
2624 483
                    if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2625 38
                        $class->reflFields[$field]->setValue($entity, $data[$field]);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2626 38
                        $this->originalEntityData[$oid][$field] = $data[$field];
2627
2628 38
                        continue;
2629
                    }
2630
2631 476
                    $associatedId = [];
2632
2633
                    // TODO: Is this even computed right in all cases of composite keys?
2634 476
                    foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
2635 476
                        $joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
2636
2637 476
                        if ($joinColumnValue !== null) {
2638 287
                            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...
2639 10
                                $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
2640
                            } else {
2641 287
                                $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...
2642
                            }
2643 278
                        } 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...
2644 278
                            && 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...
2645
                        ) {
2646
                            // the missing key is part of target's entity primary key
2647 3
                            $associatedId = [];
2648 476
                            break;
2649
                        }
2650
                    }
2651
2652 476
                    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...
2653
                        // Foreign key is NULL
2654 278
                        $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...
2655 278
                        $this->originalEntityData[$oid][$field] = null;
2656
2657 278
                        continue;
2658
                    }
2659
2660 287
                    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...
2661 284
                        $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...
2662
                    }
2663
2664
                    // Foreign key is set
2665
                    // Check identity map first
2666
                    // FIXME: Can break easily with composite keys if join column values are in
2667
                    //        wrong order. The correct order is the one in ClassMetadata#identifier.
2668 287
                    $relatedIdHash = implode(' ', $associatedId);
2669
2670
                    switch (true) {
2671 287
                        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...
2672 165
                            $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...
2673
2674
                            // If this is an uninitialized proxy, we are deferring eager loads,
2675
                            // this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
2676
                            // then we can append this entity for eager loading!
2677 165
                            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...
2678 165
                                isset($hints[self::HINT_DEFEREAGERLOAD]) &&
2679 165
                                !$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...
2680 165
                                $newValue instanceof Proxy &&
2681 165
                                $newValue->__isInitialized__ === false) {
0 ignored issues
show
Bug introduced by
Accessing __isInitialized__ on the interface Doctrine\ORM\Proxy\Proxy suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2682
2683
                                $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...
2684
                            }
2685
2686 165
                            break;
2687
2688 194
                        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...
2689
                            // If it might be a subtype, it can not be lazy. There isn't even
2690
                            // a way to solve this with deferred eager loading, which means putting
2691
                            // an entity with subclasses at a *-to-one location is really bad! (performance-wise)
2692 32
                            $newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, $associatedId);
2693 32
                            break;
2694
2695
                        default:
2696
                            switch (true) {
2697
                                // We are negating the condition here. Other cases will assume it is valid!
2698 164
                                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...
2699 157
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2700 157
                                    break;
2701
2702
                                // Deferred eager load only works for single identifier classes
2703 7
                                case (isset($hints[self::HINT_DEFEREAGERLOAD]) && ! $targetClass->isIdentifierComposite):
0 ignored issues
show
Bug introduced by
Accessing isIdentifierComposite on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2704
                                    // TODO: Is there a faster approach?
2705 7
                                    $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2706
2707 7
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2708 7
                                    break;
2709
2710
                                default:
2711
                                    // TODO: This is very imperformant, ignore it?
2712
                                    $newValue = $this->em->find($assoc['targetEntity'], $associatedId);
2713
                                    break;
2714
                            }
2715
2716
                            // PERF: Inlined & optimized code from UnitOfWork#registerManaged()
2717 164
                            $newValueOid = spl_object_hash($newValue);
2718 164
                            $this->entityIdentifiers[$newValueOid] = $associatedId;
2719 164
                            $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...
2720
2721
                            if (
2722 164
                                $newValue instanceof NotifyPropertyChanged &&
2723 164
                                ( ! $newValue instanceof Proxy || $newValue->__isInitialized())
2724
                            ) {
2725
                                $newValue->addPropertyChangedListener($this);
2726
                            }
2727 164
                            $this->entityStates[$newValueOid] = self::STATE_MANAGED;
2728
                            // make sure that when an proxy is then finally loaded, $this->originalEntityData is set also!
2729 164
                            break;
2730
                    }
2731
2732 287
                    $this->originalEntityData[$oid][$field] = $newValue;
2733 287
                    $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...
2734
2735 287
                    if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
2736 57
                        $inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']];
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2737 57
                        $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($newValue, $entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2738
                    }
2739
2740 287
                    break;
2741
2742
                default:
2743
                    // Ignore if its a cached collection
2744 476
                    if (isset($hints[Query::HINT_CACHE_ENABLED]) && $class->getFieldValue($entity, $field) instanceof PersistentCollection) {
2745
                        break;
2746
                    }
2747
2748
                    // use the given collection
2749 476
                    if (isset($data[$field]) && $data[$field] instanceof PersistentCollection) {
2750
2751 3
                        $data[$field]->setOwner($entity, $assoc);
2752
2753 3
                        $class->reflFields[$field]->setValue($entity, $data[$field]);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2754 3
                        $this->originalEntityData[$oid][$field] = $data[$field];
2755
2756 3
                        break;
2757
                    }
2758
2759
                    // Inject collection
2760 476
                    $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...
2761 476
                    $pColl->setOwner($entity, $assoc);
2762 476
                    $pColl->setInitialized(false);
2763
2764 476
                    $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...
2765 476
                    $reflField->setValue($entity, $pColl);
2766
2767 476
                    if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
2768 3
                        $this->loadCollection($pColl);
2769 3
                        $pColl->takeSnapshot();
2770
                    }
2771
2772 476
                    $this->originalEntityData[$oid][$field] = $pColl;
2773 559
                    break;
2774
            }
2775
        }
2776
2777 666
        if ($overrideLocalValues) {
2778
            // defer invoking of postLoad event to hydration complete step
2779 666
            $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...
2780
        }
2781
2782 666
        return $entity;
2783
    }
2784
2785
    /**
2786
     * @return void
2787
     */
2788 859
    public function triggerEagerLoads()
2789
    {
2790 859
        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...
2791 859
            return;
2792
        }
2793
2794
        // avoid infinite recursion
2795 7
        $eagerLoadingEntities       = $this->eagerLoadingEntities;
2796 7
        $this->eagerLoadingEntities = [];
2797
2798 7
        foreach ($eagerLoadingEntities as $entityName => $ids) {
2799 7
            if ( ! $ids) {
2800
                continue;
2801
            }
2802
2803 7
            $class = $this->em->getClassMetadata($entityName);
2804
2805 7
            $this->getEntityPersister($entityName)->loadAll(
2806 7
                array_combine($class->identifier, [array_values($ids)])
0 ignored issues
show
Bug introduced by
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2807
            );
2808
        }
2809 7
    }
2810
2811
    /**
2812
     * Initializes (loads) an uninitialized persistent collection of an entity.
2813
     *
2814
     * @param \Doctrine\ORM\PersistentCollection $collection The collection to initialize.
2815
     *
2816
     * @return void
2817
     *
2818
     * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733.
2819
     */
2820 140
    public function loadCollection(PersistentCollection $collection)
2821
    {
2822 140
        $assoc     = $collection->getMapping();
2823 140
        $persister = $this->getEntityPersister($assoc['targetEntity']);
2824
2825 140
        switch ($assoc['type']) {
2826 140
            case ClassMetadata::ONE_TO_MANY:
2827 71
                $persister->loadOneToManyCollection($assoc, $collection->getOwner(), $collection);
2828 71
                break;
2829
2830 78
            case ClassMetadata::MANY_TO_MANY:
2831 78
                $persister->loadManyToManyCollection($assoc, $collection->getOwner(), $collection);
2832 78
                break;
2833
        }
2834
2835 140
        $collection->setInitialized(true);
2836 140
    }
2837
2838
    /**
2839
     * Gets the identity map of the UnitOfWork.
2840
     *
2841
     * @return array
2842
     */
2843 1
    public function getIdentityMap()
2844
    {
2845 1
        return $this->identityMap;
2846
    }
2847
2848
    /**
2849
     * Gets the original data of an entity. The original data is the data that was
2850
     * present at the time the entity was reconstituted from the database.
2851
     *
2852
     * @param object $entity
2853
     *
2854
     * @return array
2855
     */
2856 120
    public function getOriginalEntityData($entity)
2857
    {
2858 120
        $oid = spl_object_hash($entity);
2859
2860 120
        return isset($this->originalEntityData[$oid])
2861 116
            ? $this->originalEntityData[$oid]
2862 120
            : [];
2863
    }
2864
2865
    /**
2866
     * @ignore
2867
     *
2868
     * @param object $entity
2869
     * @param array  $data
2870
     *
2871
     * @return void
2872
     */
2873
    public function setOriginalEntityData($entity, array $data)
2874
    {
2875
        $this->originalEntityData[spl_object_hash($entity)] = $data;
2876
    }
2877
2878
    /**
2879
     * INTERNAL:
2880
     * Sets a property value of the original data array of an entity.
2881
     *
2882
     * @ignore
2883
     *
2884
     * @param string $oid
2885
     * @param string $property
2886
     * @param mixed  $value
2887
     *
2888
     * @return void
2889
     */
2890 298
    public function setOriginalEntityProperty($oid, $property, $value)
2891
    {
2892 298
        $this->originalEntityData[$oid][$property] = $value;
2893 298
    }
2894
2895
    /**
2896
     * Gets the identifier of an entity.
2897
     * The returned value is always an array of identifier values. If the entity
2898
     * has a composite identifier then the identifier values are in the same
2899
     * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames().
2900
     *
2901
     * @param object $entity
2902
     *
2903
     * @return array The identifier values.
2904
     */
2905 828
    public function getEntityIdentifier($entity)
2906
    {
2907 828
        return $this->entityIdentifiers[spl_object_hash($entity)];
2908
    }
2909
2910
    /**
2911
     * Processes an entity instance to extract their identifier values.
2912
     *
2913
     * @param object $entity The entity instance.
2914
     *
2915
     * @return mixed A scalar value.
2916
     *
2917
     * @throws \Doctrine\ORM\ORMInvalidArgumentException
2918
     */
2919 119
    public function getSingleIdentifierValue($entity)
2920
    {
2921 119
        $class = $this->em->getClassMetadata(get_class($entity));
2922
2923 119
        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...
2924
            throw ORMInvalidArgumentException::invalidCompositeIdentifier();
2925
        }
2926
2927 119
        $values = $this->isInIdentityMap($entity)
2928 106
            ? $this->getEntityIdentifier($entity)
2929 119
            : $class->getIdentifierValues($entity);
2930
2931 119
        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...
2932
    }
2933
2934
    /**
2935
     * Tries to find an entity with the given identifier in the identity map of
2936
     * this UnitOfWork.
2937
     *
2938
     * @param mixed  $id            The entity identifier to look for.
2939
     * @param string $rootClassName The name of the root class of the mapped entity hierarchy.
2940
     *
2941
     * @return object|bool Returns the entity with the specified identifier if it exists in
2942
     *                     this UnitOfWork, FALSE otherwise.
2943
     */
2944 517
    public function tryGetById($id, $rootClassName)
2945
    {
2946 517
        $idHash = implode(' ', (array) $id);
2947
2948 517
        return isset($this->identityMap[$rootClassName][$idHash])
2949 80
            ? $this->identityMap[$rootClassName][$idHash]
2950 517
            : false;
2951
    }
2952
2953
    /**
2954
     * Schedules an entity for dirty-checking at commit-time.
2955
     *
2956
     * @param object $entity The entity to schedule for dirty-checking.
2957
     *
2958
     * @return void
2959
     *
2960
     * @todo Rename: scheduleForSynchronization
2961
     */
2962 5
    public function scheduleForDirtyCheck($entity)
2963
    {
2964 5
        $rootClassName = $this->em->getClassMetadata(get_class($entity))->rootEntityName;
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2965
2966 5
        $this->scheduledForSynchronization[$rootClassName][spl_object_hash($entity)] = $entity;
2967 5
    }
2968
2969
    /**
2970
     * Checks whether the UnitOfWork has any pending insertions.
2971
     *
2972
     * @return boolean TRUE if this UnitOfWork has pending insertions, FALSE otherwise.
2973
     */
2974
    public function hasPendingInsertions()
2975
    {
2976
        return ! empty($this->entityInsertions);
2977
    }
2978
2979
    /**
2980
     * Calculates the size of the UnitOfWork. The size of the UnitOfWork is the
2981
     * number of entities in the identity map.
2982
     *
2983
     * @return integer
2984
     */
2985 1
    public function size()
2986
    {
2987 1
        $countArray = array_map('count', $this->identityMap);
2988
2989 1
        return array_sum($countArray);
2990
    }
2991
2992
    /**
2993
     * Gets the EntityPersister for an Entity.
2994
     *
2995
     * @param string $entityName The name of the Entity.
2996
     *
2997
     * @return \Doctrine\ORM\Persisters\Entity\EntityPersister
2998
     */
2999 1053
    public function getEntityPersister($entityName)
3000
    {
3001 1053
        if (isset($this->persisters[$entityName])) {
3002 829
            return $this->persisters[$entityName];
3003
        }
3004
3005 1053
        $class = $this->em->getClassMetadata($entityName);
3006
3007
        switch (true) {
3008 1053
            case ($class->isInheritanceTypeNone()):
3009 1021
                $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...
3010 1021
                break;
3011
3012 363
            case ($class->isInheritanceTypeSingleTable()):
3013 223
                $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...
3014 223
                break;
3015
3016 332
            case ($class->isInheritanceTypeJoined()):
3017 332
                $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...
3018 332
                break;
3019
3020
            default:
3021
                throw new \RuntimeException('No persister found for entity.');
3022
        }
3023
3024 1053
        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...
3025 123
            $persister = $this->em->getConfiguration()
3026 123
                ->getSecondLevelCacheConfiguration()
3027 123
                ->getCacheFactory()
3028 123
                ->buildCachedEntityPersister($this->em, $persister, $class);
3029
        }
3030
3031 1053
        $this->persisters[$entityName] = $persister;
3032
3033 1053
        return $this->persisters[$entityName];
3034
    }
3035
3036
    /**
3037
     * Gets a collection persister for a collection-valued association.
3038
     *
3039
     * @param array $association
3040
     *
3041
     * @return \Doctrine\ORM\Persisters\Collection\CollectionPersister
3042
     */
3043 559
    public function getCollectionPersister(array $association)
3044
    {
3045 559
        $role = isset($association['cache'])
3046 78
            ? $association['sourceEntity'] . '::' . $association['fieldName']
3047 559
            : $association['type'];
3048
3049 559
        if (isset($this->collectionPersisters[$role])) {
3050 438
            return $this->collectionPersisters[$role];
3051
        }
3052
3053 559
        $persister = ClassMetadata::ONE_TO_MANY === $association['type']
3054 398
            ? new OneToManyPersister($this->em)
3055 559
            : new ManyToManyPersister($this->em);
3056
3057 559
        if ($this->hasCache && isset($association['cache'])) {
3058 77
            $persister = $this->em->getConfiguration()
3059 77
                ->getSecondLevelCacheConfiguration()
3060 77
                ->getCacheFactory()
3061 77
                ->buildCachedCollectionPersister($this->em, $persister, $association);
3062
        }
3063
3064 559
        $this->collectionPersisters[$role] = $persister;
3065
3066 559
        return $this->collectionPersisters[$role];
3067
    }
3068
3069
    /**
3070
     * INTERNAL:
3071
     * Registers an entity as managed.
3072
     *
3073
     * @param object $entity The entity.
3074
     * @param array  $id     The identifier values.
3075
     * @param array  $data   The original entity data.
3076
     *
3077
     * @return void
3078
     */
3079 206
    public function registerManaged($entity, array $id, array $data)
3080
    {
3081 206
        $oid = spl_object_hash($entity);
3082
3083 206
        $this->entityIdentifiers[$oid]  = $id;
3084 206
        $this->entityStates[$oid]       = self::STATE_MANAGED;
3085 206
        $this->originalEntityData[$oid] = $data;
3086
3087 206
        $this->addToIdentityMap($entity);
3088
3089 200
        if ($entity instanceof NotifyPropertyChanged && ( ! $entity instanceof Proxy || $entity->__isInitialized())) {
3090 2
            $entity->addPropertyChangedListener($this);
3091
        }
3092 200
    }
3093
3094
    /**
3095
     * INTERNAL:
3096
     * Clears the property changeset of the entity with the given OID.
3097
     *
3098
     * @param string $oid The entity's OID.
3099
     *
3100
     * @return void
3101
     */
3102
    public function clearEntityChangeSet($oid)
3103
    {
3104
        $this->entityChangeSets[$oid] = [];
3105
    }
3106
3107
    /* PropertyChangedListener implementation */
3108
3109
    /**
3110
     * Notifies this UnitOfWork of a property change in an entity.
3111
     *
3112
     * @param object $entity       The entity that owns the property.
3113
     * @param string $propertyName The name of the property that changed.
3114
     * @param mixed  $oldValue     The old value of the property.
3115
     * @param mixed  $newValue     The new value of the property.
3116
     *
3117
     * @return void
3118
     */
3119 3
    public function propertyChanged($entity, $propertyName, $oldValue, $newValue)
3120
    {
3121 3
        $oid   = spl_object_hash($entity);
3122 3
        $class = $this->em->getClassMetadata(get_class($entity));
3123
3124 3
        $isAssocField = isset($class->associationMappings[$propertyName]);
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
3125
3126 3
        if ( ! $isAssocField && ! isset($class->fieldMappings[$propertyName])) {
0 ignored issues
show
Bug introduced by
Accessing fieldMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
3127 1
            return; // ignore non-persistent fields
3128
        }
3129
3130
        // Update changeset and mark entity for synchronization
3131 3
        $this->entityChangeSets[$oid][$propertyName] = [$oldValue, $newValue];
3132
3133 3
        if ( ! isset($this->scheduledForSynchronization[$class->rootEntityName][$oid])) {
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
3134 3
            $this->scheduleForDirtyCheck($entity);
3135
        }
3136 3
    }
3137
3138
    /**
3139
     * Gets the currently scheduled entity insertions in this UnitOfWork.
3140
     *
3141
     * @return array
3142
     */
3143 2
    public function getScheduledEntityInsertions()
3144
    {
3145 2
        return $this->entityInsertions;
3146
    }
3147
3148
    /**
3149
     * Gets the currently scheduled entity updates in this UnitOfWork.
3150
     *
3151
     * @return array
3152
     */
3153 2
    public function getScheduledEntityUpdates()
3154
    {
3155 2
        return $this->entityUpdates;
3156
    }
3157
3158
    /**
3159
     * Gets the currently scheduled entity deletions in this UnitOfWork.
3160
     *
3161
     * @return array
3162
     */
3163 1
    public function getScheduledEntityDeletions()
3164
    {
3165 1
        return $this->entityDeletions;
3166
    }
3167
3168
    /**
3169
     * Gets the currently scheduled complete collection deletions
3170
     *
3171
     * @return array
3172
     */
3173 1
    public function getScheduledCollectionDeletions()
3174
    {
3175 1
        return $this->collectionDeletions;
3176
    }
3177
3178
    /**
3179
     * Gets the currently scheduled collection inserts, updates and deletes.
3180
     *
3181
     * @return array
3182
     */
3183
    public function getScheduledCollectionUpdates()
3184
    {
3185
        return $this->collectionUpdates;
3186
    }
3187
3188
    /**
3189
     * Helper method to initialize a lazy loading proxy or persistent collection.
3190
     *
3191
     * @param object $obj
3192
     *
3193
     * @return void
3194
     */
3195 2
    public function initializeObject($obj)
3196
    {
3197 2
        if ($obj instanceof Proxy) {
3198 1
            $obj->__load();
3199
3200 1
            return;
3201
        }
3202
3203 1
        if ($obj instanceof PersistentCollection) {
3204 1
            $obj->initialize();
3205
        }
3206 1
    }
3207
3208
    /**
3209
     * Helper method to show an object as string.
3210
     *
3211
     * @param object $obj
3212
     *
3213
     * @return string
3214
     */
3215 1
    private static function objToStr($obj)
3216
    {
3217 1
        return method_exists($obj, '__toString') ? (string) $obj : get_class($obj).'@'.spl_object_hash($obj);
3218
    }
3219
3220
    /**
3221
     * Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit().
3222
     *
3223
     * This operation cannot be undone as some parts of the UnitOfWork now keep gathering information
3224
     * on this object that might be necessary to perform a correct update.
3225
     *
3226
     * @param object $object
3227
     *
3228
     * @return void
3229
     *
3230
     * @throws ORMInvalidArgumentException
3231
     */
3232 6
    public function markReadOnly($object)
3233
    {
3234 6
        if ( ! is_object($object) || ! $this->isInIdentityMap($object)) {
3235 1
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3236
        }
3237
3238 5
        $this->readOnlyObjects[spl_object_hash($object)] = true;
3239 5
    }
3240
3241
    /**
3242
     * Is this entity read only?
3243
     *
3244
     * @param object $object
3245
     *
3246
     * @return bool
3247
     *
3248
     * @throws ORMInvalidArgumentException
3249
     */
3250 3
    public function isReadOnly($object)
3251
    {
3252 3
        if ( ! is_object($object)) {
3253
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3254
        }
3255
3256 3
        return isset($this->readOnlyObjects[spl_object_hash($object)]);
3257
    }
3258
3259
    /**
3260
     * Perform whatever processing is encapsulated here after completion of the transaction.
3261
     */
3262 983
    private function afterTransactionComplete()
3263
    {
3264
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3265 93
            $persister->afterTransactionComplete();
3266 983
        });
3267 983
    }
3268
3269
    /**
3270
     * Perform whatever processing is encapsulated here after completion of the rolled-back.
3271
     */
3272
    private function afterTransactionRolledBack()
3273
    {
3274 15
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3275 3
            $persister->afterTransactionRolledBack();
3276 15
        });
3277 15
    }
3278
3279
    /**
3280
     * Performs an action after the transaction.
3281
     *
3282
     * @param callable $callback
3283
     */
3284 988
    private function performCallbackOnCachedPersister(callable $callback)
3285
    {
3286 988
        if ( ! $this->hasCache) {
3287 895
            return;
3288
        }
3289
3290 93
        foreach (array_merge($this->persisters, $this->collectionPersisters) as $persister) {
3291 93
            if ($persister instanceof CachedPersister) {
3292 93
                $callback($persister);
3293
            }
3294
        }
3295 93
    }
3296
3297 992
    private function dispatchOnFlushEvent()
3298
    {
3299 992
        if ($this->evm->hasListeners(Events::onFlush)) {
3300 4
            $this->evm->dispatchEvent(Events::onFlush, new OnFlushEventArgs($this->em));
3301
        }
3302 992
    }
3303
3304 987
    private function dispatchPostFlushEvent()
3305
    {
3306 987
        if ($this->evm->hasListeners(Events::postFlush)) {
3307 5
            $this->evm->dispatchEvent(Events::postFlush, new PostFlushEventArgs($this->em));
3308
        }
3309 986
    }
3310
3311
    /**
3312
     * Verifies if two given entities actually are the same based on identifier comparison
3313
     *
3314
     * @param object $entity1
3315
     * @param object $entity2
3316
     *
3317
     * @return bool
3318
     */
3319 14
    private function isIdentifierEquals($entity1, $entity2)
3320
    {
3321 14
        if ($entity1 === $entity2) {
3322
            return true;
3323
        }
3324
3325 14
        $class = $this->em->getClassMetadata(get_class($entity1));
3326
3327 14
        if ($class !== $this->em->getClassMetadata(get_class($entity2))) {
3328 11
            return false;
3329
        }
3330
3331 3
        $oid1 = spl_object_hash($entity1);
3332 3
        $oid2 = spl_object_hash($entity2);
3333
3334 3
        $id1 = isset($this->entityIdentifiers[$oid1])
3335 3
            ? $this->entityIdentifiers[$oid1]
3336 3
            : $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($entity1));
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

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

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

Loading history...
3337 3
        $id2 = isset($this->entityIdentifiers[$oid2])
3338 3
            ? $this->entityIdentifiers[$oid2]
3339 3
            : $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($entity2));
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

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

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

Loading history...
3340
3341 3
        return $id1 === $id2 || implode(' ', $id1) === implode(' ', $id2);
3342
    }
3343
3344
    /**
3345
     * @param object $entity
3346
     * @param object $managedCopy
3347
     *
3348
     * @throws ORMException
3349
     * @throws OptimisticLockException
3350
     * @throws TransactionRequiredException
3351
     */
3352 39
    private function mergeEntityStateIntoManagedCopy($entity, $managedCopy)
3353
    {
3354 39
        if (! $this->isLoaded($entity)) {
3355 7
            return;
3356
        }
3357
3358 32
        if (! $this->isLoaded($managedCopy)) {
3359 4
            $managedCopy->__load();
3360
        }
3361
3362 32
        $class = $this->em->getClassMetadata(get_class($entity));
3363
3364 32
        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...
3365 32
            $name = $prop->name;
3366
3367 32
            $prop->setAccessible(true);
3368
3369 32
            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...
3370 32
                if ( ! $class->isIdentifier($name)) {
3371 32
                    $prop->setValue($managedCopy, $prop->getValue($entity));
3372
                }
3373
            } else {
3374 28
                $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...
3375
3376 28
                if ($assoc2['type'] & ClassMetadata::TO_ONE) {
3377 24
                    $other = $prop->getValue($entity);
3378 24
                    if ($other === null) {
3379 11
                        $prop->setValue($managedCopy, null);
3380
                    } else {
3381 15
                        if ($other instanceof Proxy && !$other->__isInitialized()) {
3382
                            // do not merge fields marked lazy that have not been fetched.
3383 4
                            continue;
3384
                        }
3385
3386 11
                        if ( ! $assoc2['isCascadeMerge']) {
3387 6
                            if ($this->getEntityState($other) === self::STATE_DETACHED) {
3388 3
                                $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']);
3389 3
                                $relatedId   = $targetClass->getIdentifierValues($other);
3390
3391 3
                                if ($targetClass->subClasses) {
0 ignored issues
show
Bug introduced by
Accessing subClasses on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
3393
                                } else {
3394 1
                                    $other = $this->em->getProxyFactory()->getProxy(
3395 1
                                        $assoc2['targetEntity'],
3396 1
                                        $relatedId
3397
                                    );
3398 1
                                    $this->registerManaged($other, $relatedId, []);
3399
                                }
3400
                            }
3401
3402 20
                            $prop->setValue($managedCopy, $other);
3403
                        }
3404
                    }
3405
                } else {
3406 16
                    $mergeCol = $prop->getValue($entity);
3407
3408 16
                    if ($mergeCol instanceof PersistentCollection && ! $mergeCol->isInitialized()) {
3409
                        // do not merge fields marked lazy that have not been fetched.
3410
                        // keep the lazy persistent collection of the managed copy.
3411 5
                        continue;
3412
                    }
3413
3414 13
                    $managedCol = $prop->getValue($managedCopy);
3415
3416 13
                    if ( ! $managedCol) {
3417 4
                        $managedCol = new PersistentCollection(
3418 4
                            $this->em,
3419 4
                            $this->em->getClassMetadata($assoc2['targetEntity']),
0 ignored issues
show
Compatibility introduced by
$this->em->getClassMetad...assoc2['targetEntity']) of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

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

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

Loading history...
3420 4
                            new ArrayCollection
3421
                        );
3422 4
                        $managedCol->setOwner($managedCopy, $assoc2);
3423 4
                        $prop->setValue($managedCopy, $managedCol);
3424
                    }
3425
3426 13
                    if ($assoc2['isCascadeMerge']) {
3427 8
                        $managedCol->initialize();
3428
3429
                        // clear and set dirty a managed collection if its not also the same collection to merge from.
3430 8
                        if ( ! $managedCol->isEmpty() && $managedCol !== $mergeCol) {
3431
                            $managedCol->unwrap()->clear();
3432
                            $managedCol->setDirty(true);
3433
3434
                            if ($assoc2['isOwningSide']
3435
                                && $assoc2['type'] == ClassMetadata::MANY_TO_MANY
3436
                                && $class->isChangeTrackingNotify()
3437
                            ) {
3438
                                $this->scheduleForDirtyCheck($managedCopy);
3439
                            }
3440
                        }
3441
                    }
3442
                }
3443
            }
3444
3445 32
            if ($class->isChangeTrackingNotify()) {
3446
                // Just treat all properties as changed, there is no other choice.
3447 32
                $this->propertyChanged($managedCopy, $name, null, $prop->getValue($managedCopy));
3448
            }
3449
        }
3450 32
    }
3451
3452
    /**
3453
     * This method called by hydrators, and indicates that hydrator totally completed current hydration cycle.
3454
     * Unit of work able to fire deferred events, related to loading events here.
3455
     *
3456
     * @internal should be called internally from object hydrators
3457
     */
3458 872
    public function hydrationComplete()
3459
    {
3460 872
        $this->hydrationCompleteHandler->hydrationComplete();
3461 872
    }
3462
3463
    /**
3464
     * @param string $entityName
3465
     */
3466 4
    private function clearIdentityMapForEntityName($entityName)
3467
    {
3468 4
        if (! isset($this->identityMap[$entityName])) {
3469
            return;
3470
        }
3471
3472 4
        $visited = [];
3473
3474 4
        foreach ($this->identityMap[$entityName] as $entity) {
3475 4
            $this->doDetach($entity, $visited, false);
3476
        }
3477 4
    }
3478
3479
    /**
3480
     * @param string $entityName
3481
     */
3482 4
    private function clearEntityInsertionsForEntityName($entityName)
3483
    {
3484 4
        foreach ($this->entityInsertions as $hash => $entity) {
3485
            // note: performance optimization - `instanceof` is much faster than a function call
3486 1
            if ($entity instanceof $entityName && get_class($entity) === $entityName) {
3487 1
                unset($this->entityInsertions[$hash]);
3488
            }
3489
        }
3490 4
    }
3491
3492
    /**
3493
     * @param ClassMetadata $class
3494
     * @param mixed         $identifierValue
3495
     *
3496
     * @return mixed the identifier after type conversion
3497
     *
3498
     * @throws \Doctrine\ORM\Mapping\MappingException if the entity has more than a single identifier
3499
     */
3500 898
    private function convertSingleFieldIdentifierToPHPValue(ClassMetadata $class, $identifierValue)
3501
    {
3502 898
        return $this->em->getConnection()->convertToPHPValue(
3503
            $identifierValue,
3504 898
            $class->getTypeOfField($class->getSingleIdentifierFieldName())
0 ignored issues
show
Bug introduced by
It seems like $class->getTypeOfField($...eIdentifierFieldName()) targeting Doctrine\ORM\Mapping\Cla...aInfo::getTypeOfField() can also be of type null or object<Doctrine\DBAL\Types\Type>; however, Doctrine\DBAL\Connection::convertToPHPValue() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

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

An additional type check may prevent trouble.

Loading history...
3505
        );
3506
    }
3507
}
3508