Completed
Pull Request — master (#6038)
by Reen
12:51
created

UnitOfWork::addToIdentityMap()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

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

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

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

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

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

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

Loading history...
345 1016
                $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...
346 25
            $this->dispatchOnFlushEvent();
347 25
            $this->dispatchPostFlushEvent();
348
349 25
            return; // Nothing to do.
350
        }
351
352 1012
        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...
353 16
            foreach ($this->orphanRemovals as $orphan) {
354 16
                $this->remove($orphan);
355
            }
356
        }
357
358 1012
        $this->dispatchOnFlushEvent();
359
360
        // Now we need a commit order to maintain referential integrity
361 1012
        $commitOrder = $this->getCommitOrder();
362
363 1012
        $conn = $this->em->getConnection();
364 1012
        $conn->beginTransaction();
365
366
        try {
367
            // Collection deletions (deletions of complete collections)
368 1012
            foreach ($this->collectionDeletions as $collectionToDelete) {
369 19
                $this->getCollectionPersister($collectionToDelete->getMapping())->delete($collectionToDelete);
370
            }
371
372 1012
            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...
373 1008
                foreach ($commitOrder as $class) {
374 1008
                    $this->executeInserts($class);
375
                }
376
            }
377
378 1011
            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...
379 116
                foreach ($commitOrder as $class) {
380 116
                    $this->executeUpdates($class);
381
                }
382
            }
383
384
            // Extra updates that were requested by persisters.
385 1007
            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...
386 40
                $this->executeExtraUpdates();
387
            }
388
389
            // Collection updates (deleteRows, updateRows, insertRows)
390 1007
            foreach ($this->collectionUpdates as $collectionToUpdate) {
391 531
                $this->getCollectionPersister($collectionToUpdate->getMapping())->update($collectionToUpdate);
392
            }
393
394
            // Entity deletions come last and need to be in reverse commit order
395 1007
            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...
396 63
                for ($count = count($commitOrder), $i = $count - 1; $i >= 0 && $this->entityDeletions; --$i) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->entityDeletions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
397 63
                    $this->executeDeletions($commitOrder[$i]);
398
                }
399
            }
400
401 1007
            $conn->commit();
402 11
        } catch (Exception $e) {
403 11
            $this->em->close();
404 11
            $conn->rollBack();
405
406 11
            $this->afterTransactionRolledBack();
407
408 11
            throw $e;
409
        }
410
411 1007
        $this->afterTransactionComplete();
412
413
        // Take new snapshots from visited collections
414 1007
        foreach ($this->visitedCollections as $coll) {
415 530
            $coll->takeSnapshot();
416
        }
417
418 1007
        $this->dispatchPostFlushEvent();
419
420
        // Clear up
421 1006
        $this->entityInsertions =
422 1006
        $this->entityUpdates =
423 1006
        $this->entityDeletions =
424 1006
        $this->extraUpdates =
425 1006
        $this->entityChangeSets =
426 1006
        $this->collectionUpdates =
427 1006
        $this->collectionDeletions =
428 1006
        $this->visitedCollections =
429 1006
        $this->scheduledForSynchronization =
430 1006
        $this->orphanRemovals = array();
431 1006
    }
432
433
    /**
434
     * Computes the changesets of all entities scheduled for insertion.
435
     *
436
     * @return void
437
     */
438 1018
    private function computeScheduleInsertsChangeSets()
439
    {
440 1018
        foreach ($this->entityInsertions as $entity) {
441 1010
            $class = $this->em->getClassMetadata(get_class($entity));
442
443 1010
            $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...
444
        }
445 1016
    }
446
447
    /**
448
     * Only flushes the given entity according to a ruleset that keeps the UoW consistent.
449
     *
450
     * 1. All entities scheduled for insertion, (orphan) removals and changes in collections are processed as well!
451
     * 2. Read Only entities are skipped.
452
     * 3. Proxies are skipped.
453
     * 4. Only if entity is properly managed.
454
     *
455
     * @param object $entity
456
     *
457
     * @return void
458
     *
459
     * @throws \InvalidArgumentException
460
     */
461 16
    private function computeSingleEntityChangeSet($entity)
462
    {
463 16
        $state = $this->getEntityState($entity);
464
465 16
        if ($state !== self::STATE_MANAGED && $state !== self::STATE_REMOVED) {
466 1
            throw new \InvalidArgumentException("Entity has to be managed or scheduled for removal for single computation " . self::objToStr($entity));
467
        }
468
469 15
        $class = $this->em->getClassMetadata(get_class($entity));
470
471 15
        if ($state === self::STATE_MANAGED && $class->isChangeTrackingDeferredImplicit()) {
472 14
            $this->persist($entity);
473
        }
474
475
        // Compute changes for INSERTed entities first. This must always happen even in this case.
476 15
        $this->computeScheduleInsertsChangeSets();
477
478 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...
479
            return;
480
        }
481
482
        // Ignore uninitialized proxy objects
483 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...
484 2
            return;
485
        }
486
487
        // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here.
488 13
        $oid = spl_object_hash($entity);
489
490 13
        if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) {
491 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...
492
        }
493 12
    }
494
495
    /**
496
     * Executes any extra updates that have been scheduled.
497
     */
498 40
    private function executeExtraUpdates()
499
    {
500 40
        foreach ($this->extraUpdates as $oid => $update) {
501 40
            list ($entity, $changeset) = $update;
502
503 40
            $this->entityChangeSets[$oid] = $changeset;
504 40
            $this->getEntityPersister(get_class($entity))->update($entity);
505
        }
506
507 40
        $this->extraUpdates = array();
508 40
    }
509
510
    /**
511
     * Gets the changeset for an entity.
512
     *
513
     * @param object $entity
514
     *
515
     * @return array
516
     */
517 1010
    public function & getEntityChangeSet($entity)
518
    {
519 1010
        $oid  = spl_object_hash($entity);
520 1010
        $data = array();
521
522 1010
        if (!isset($this->entityChangeSets[$oid])) {
523 1
            return $data;
524
        }
525
526 1010
        return $this->entityChangeSets[$oid];
527
    }
528
529
    /**
530
     * Computes the changes that happened to a single entity.
531
     *
532
     * Modifies/populates the following properties:
533
     *
534
     * {@link _originalEntityData}
535
     * If the entity is NEW or MANAGED but not yet fully persisted (only has an id)
536
     * then it was not fetched from the database and therefore we have no original
537
     * entity data yet. All of the current entity data is stored as the original entity data.
538
     *
539
     * {@link _entityChangeSets}
540
     * The changes detected on all properties of the entity are stored there.
541
     * A change is a tuple array where the first entry is the old value and the second
542
     * entry is the new value of the property. Changesets are used by persisters
543
     * to INSERT/UPDATE the persistent entity state.
544
     *
545
     * {@link _entityUpdates}
546
     * If the entity is already fully MANAGED (has been fetched from the database before)
547
     * and any changes to its properties are detected, then a reference to the entity is stored
548
     * there to mark it for an update.
549
     *
550
     * {@link _collectionDeletions}
551
     * If a PersistentCollection has been de-referenced in a fully MANAGED entity,
552
     * then this collection is marked for deletion.
553
     *
554
     * @ignore
555
     *
556
     * @internal Don't call from the outside.
557
     *
558
     * @param ClassMetadata $class  The class descriptor of the entity.
559
     * @param object        $entity The entity for which to compute the changes.
560
     *
561
     * @return void
562
     */
563 1020
    public function computeChangeSet(ClassMetadata $class, $entity)
564
    {
565 1020
        $oid = spl_object_hash($entity);
566
567 1020
        if (isset($this->readOnlyObjects[$oid])) {
568 2
            return;
569
        }
570
571 1020
        if ( ! $class->isInheritanceTypeNone()) {
572 308
            $class = $this->em->getClassMetadata(get_class($entity));
573
        }
574
575 1020
        $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...
576
577 1020
        if ($invoke !== ListenersInvoker::INVOKE_NONE) {
578 137
            $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...
579
        }
580
581 1020
        $actualData = array();
582
583 1020
        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...
584 1020
            $value = $refProp->getValue($entity);
585
586 1020
            if ($class->isCollectionValuedAssociation($name) && $value !== null) {
587 779
                if ($value instanceof PersistentCollection) {
588 199
                    if ($value->getOwner() === $entity) {
589 199
                        continue;
590
                    }
591
592 5
                    $value = new ArrayCollection($value->getValues());
593
                }
594
595
                // If $value is not a Collection then use an ArrayCollection.
596 774
                if ( ! $value instanceof Collection) {
597 242
                    $value = new ArrayCollection($value);
598
                }
599
600 774
                $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...
601
602
                // Inject PersistentCollection
603 774
                $value = new PersistentCollection(
604 774
                    $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...
605
                );
606 774
                $value->setOwner($entity, $assoc);
607 774
                $value->setDirty( ! $value->isEmpty());
608
609 774
                $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...
610
611 774
                $actualData[$name] = $value;
612
613 774
                continue;
614
            }
615
616 1020
            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...
617 1020
                $actualData[$name] = $value;
618
            }
619
        }
620
621 1020
        if ( ! isset($this->originalEntityData[$oid])) {
622
            // Entity is either NEW or MANAGED but not yet fully persisted (only has an id).
623
            // These result in an INSERT.
624 1016
            $this->originalEntityData[$oid] = $actualData;
625 1016
            $changeSet = array();
626
627 1016
            foreach ($actualData as $propName => $actualValue) {
628 1001
                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...
629 950
                    $changeSet[$propName] = array(null, $actualValue);
630
631 950
                    continue;
632
                }
633
634 896
                $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...
635
636 896
                if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
637 896
                    $changeSet[$propName] = array(null, $actualValue);
638
                }
639
            }
640
641 1016
            $this->entityChangeSets[$oid] = $changeSet;
642
        } else {
643
            // Entity is "fully" MANAGED: it was already fully persisted before
644
            // and we have a copy of the original data
645 264
            $originalData           = $this->originalEntityData[$oid];
646 264
            $isChangeTrackingNotify = $class->isChangeTrackingNotify();
647 264
            $changeSet              = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid]))
648
                ? $this->entityChangeSets[$oid]
649 264
                : array();
650
651 264
            foreach ($actualData as $propName => $actualValue) {
652
                // skip field, its a partially omitted one!
653 249
                if ( ! (isset($originalData[$propName]) || array_key_exists($propName, $originalData))) {
654 8
                    continue;
655
                }
656
657 249
                $orgValue = $originalData[$propName];
658
659
                // skip if value haven't changed
660 249
                if ($orgValue === $actualValue) {
661 233
                    continue;
662
                }
663
664
                // if regular field
665 112
                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...
666 58
                    if ($isChangeTrackingNotify) {
667
                        continue;
668
                    }
669
670 58
                    $changeSet[$propName] = array($orgValue, $actualValue);
671
672 58
                    continue;
673
                }
674
675 58
                $assoc = $class->associationMappings[$propName];
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
676
677
                // Persistent collection was exchanged with the "originally"
678
                // created one. This can only mean it was cloned and replaced
679
                // on another entity.
680 58
                if ($actualValue instanceof PersistentCollection) {
681 8
                    $owner = $actualValue->getOwner();
682 8
                    if ($owner === null) { // cloned
683
                        $actualValue->setOwner($entity, $assoc);
684 8
                    } else if ($owner !== $entity) { // no clone, we have to fix
685
                        if (!$actualValue->isInitialized()) {
686
                            $actualValue->initialize(); // we have to do this otherwise the cols share state
687
                        }
688
                        $newValue = clone $actualValue;
689
                        $newValue->setOwner($entity, $assoc);
690
                        $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...
691
                    }
692
                }
693
694 58
                if ($orgValue instanceof PersistentCollection) {
695
                    // A PersistentCollection was de-referenced, so delete it.
696 8
                    $coid = spl_object_hash($orgValue);
697
698 8
                    if (isset($this->collectionDeletions[$coid])) {
699
                        continue;
700
                    }
701
702 8
                    $this->collectionDeletions[$coid] = $orgValue;
703 8
                    $changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored.
704
705 8
                    continue;
706
                }
707
708 50
                if ($assoc['type'] & ClassMetadata::TO_ONE) {
709 49
                    if ($assoc['isOwningSide']) {
710 21
                        $changeSet[$propName] = array($orgValue, $actualValue);
711
                    }
712
713 49
                    if ($orgValue !== null && $assoc['orphanRemoval']) {
714 50
                        $this->scheduleOrphanRemoval($orgValue);
715
                    }
716
                }
717
            }
718
719 264
            if ($changeSet) {
720 85
                $this->entityChangeSets[$oid]   = $changeSet;
721 85
                $this->originalEntityData[$oid] = $actualData;
722 85
                $this->entityUpdates[$oid]      = $entity;
723
            }
724
        }
725
726
        // Look for changes in associations of the entity
727 1020
        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...
728 896
            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...
729 640
                continue;
730
            }
731
732 867
            $this->computeAssociationChanges($assoc, $val);
733
734 859
            if ( ! isset($this->entityChangeSets[$oid]) &&
735 859
                $assoc['isOwningSide'] &&
736 859
                $assoc['type'] == ClassMetadata::MANY_TO_MANY &&
737 859
                $val instanceof PersistentCollection &&
738 859
                $val->isDirty()) {
739
740 35
                $this->entityChangeSets[$oid]   = array();
741 35
                $this->originalEntityData[$oid] = $actualData;
742 859
                $this->entityUpdates[$oid]      = $entity;
743
            }
744
        }
745 1012
    }
746
747
    /**
748
     * Computes all the changes that have been done to entities and collections
749
     * since the last commit and stores these changes in the _entityChangeSet map
750
     * temporarily for access by the persisters, until the UoW commit is finished.
751
     *
752
     * @return void
753
     */
754 1011
    public function computeChangeSets()
755
    {
756
        // Compute changes for INSERTed entities first. This must always happen.
757 1011
        $this->computeScheduleInsertsChangeSets();
758
759
        // Compute changes for other MANAGED entities. Change tracking policies take effect here.
760 1009
        foreach ($this->identityMap as $className => $entities) {
761 449
            $class = $this->em->getClassMetadata($className);
762
763
            // Skip class if instances are read-only
764 449
            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...
765 1
                continue;
766
            }
767
768
            // If change tracking is explicit or happens through notification, then only compute
769
            // changes on entities of that type that are explicitly marked for synchronization.
770
            switch (true) {
771 448
                case ($class->isChangeTrackingDeferredImplicit()):
772 446
                    $entitiesToProcess = $entities;
773 446
                    break;
774
775 3
                case (isset($this->scheduledForSynchronization[$className])):
776 3
                    $entitiesToProcess = $this->scheduledForSynchronization[$className];
777 3
                    break;
778
779
                default:
780 1
                    $entitiesToProcess = array();
781
782
            }
783
784 448
            foreach ($entitiesToProcess as $entity) {
785
                // Ignore uninitialized proxy objects
786 428
                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...
787 34
                    continue;
788
                }
789
790
                // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here.
791 427
                $oid = spl_object_hash($entity);
792
793 427
                if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) {
794 448
                    $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...
795
                }
796
            }
797
        }
798 1009
    }
799
800
    /**
801
     * Computes the changes of an association.
802
     *
803
     * @param array $assoc The association mapping.
804
     * @param mixed $value The value of the association.
805
     *
806
     * @throws ORMInvalidArgumentException
807
     * @throws ORMException
808
     *
809
     * @return void
810
     */
811 867
    private function computeAssociationChanges($assoc, $value)
812
    {
813 867
        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...
814 27
            return;
815
        }
816
817 866
        if ($value instanceof PersistentCollection && $value->isDirty()) {
818 533
            $coid = spl_object_hash($value);
819
820 533
            $this->collectionUpdates[$coid] = $value;
821 533
            $this->visitedCollections[$coid] = $value;
822
        }
823
824
        // Look through the entities, and in any of their associations,
825
        // for transient (new) entities, recursively. ("Persistence by reachability")
826
        // Unwrap. Uninitialized collections will simply be empty.
827 866
        $unwrappedValue = ($assoc['type'] & ClassMetadata::TO_ONE) ? array($value) : $value->unwrap();
828 866
        $targetClass    = $this->em->getClassMetadata($assoc['targetEntity']);
829
830 866
        foreach ($unwrappedValue as $key => $entry) {
831 722
            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...
832 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...
833
            }
834
835 716
            $state = $this->getEntityState($entry, self::STATE_NEW);
836
837 716
            if ( ! ($entry instanceof $assoc['targetEntity'])) {
838
                throw ORMException::unexpectedAssociationValue($assoc['sourceEntity'], $assoc['fieldName'], get_class($entry), $assoc['targetEntity']);
839
            }
840
841
            switch ($state) {
842 716
                case self::STATE_NEW:
843 39
                    if ( ! $assoc['isCascadePersist']) {
844 4
                        throw ORMInvalidArgumentException::newEntityFoundThroughRelationship($assoc, $entry);
845
                    }
846
847 35
                    $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...
848 35
                    $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...
849 35
                    break;
850
851 710
                case self::STATE_REMOVED:
852
                    // Consume the $value as array (it's either an array or an ArrayAccess)
853
                    // and remove the element from Collection.
854 4
                    if ($assoc['type'] & ClassMetadata::TO_MANY) {
855 3
                        unset($value[$key]);
856
                    }
857 4
                    break;
858
859 710
                case self::STATE_DETACHED:
860
                    // Can actually not happen right now as we assume STATE_NEW,
861
                    // so the exception will be raised from the DBAL layer (constraint violation).
862
                    throw ORMInvalidArgumentException::detachedEntityFoundThroughRelationship($assoc, $entry);
863
                    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...
864
865 713
                default:
866
                    // MANAGED associated entities are already taken into account
867
                    // during changeset calculation anyway, since they are in the identity map.
868
            }
869
        }
870 858
    }
871
872
    /**
873
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
874
     * @param object                              $entity
875
     *
876
     * @return void
877
     */
878 1036
    private function persistNew($class, $entity)
879
    {
880 1036
        $oid    = spl_object_hash($entity);
881 1036
        $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::prePersist);
882
883 1036
        if ($invoke !== ListenersInvoker::INVOKE_NONE) {
884 139
            $this->listenersInvoker->invoke($class, Events::prePersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
885
        }
886
887 1036
        $idGen = $class->idGenerator;
888
889 1036
        if ( ! $idGen->isPostInsertGenerator()) {
890 269
            $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...
891
892 269
            if ( ! $idGen instanceof \Doctrine\ORM\Id\AssignedGenerator) {
893 1
                $idValue = array($class->identifier[0] => $idValue);
894
895 1
                $class->setIdentifierValues($entity, $idValue);
896
            }
897
898 269
            $this->entityIdentifiers[$oid] = $idValue;
899
        }
900
901 1036
        $this->entityStates[$oid] = self::STATE_MANAGED;
902
903 1036
        $this->scheduleForInsert($entity);
904 1036
    }
905
906
    /**
907
     * INTERNAL:
908
     * Computes the changeset of an individual entity, independently of the
909
     * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit().
910
     *
911
     * The passed entity must be a managed entity. If the entity already has a change set
912
     * because this method is invoked during a commit cycle then the change sets are added.
913
     * whereby changes detected in this method prevail.
914
     *
915
     * @ignore
916
     *
917
     * @param ClassMetadata $class  The class descriptor of the entity.
918
     * @param object        $entity The entity for which to (re)calculate the change set.
919
     *
920
     * @return void
921
     *
922
     * @throws ORMInvalidArgumentException If the passed entity is not MANAGED.
923
     */
924 16
    public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity)
925
    {
926 16
        $oid = spl_object_hash($entity);
927
928 16
        if ( ! isset($this->entityStates[$oid]) || $this->entityStates[$oid] != self::STATE_MANAGED) {
929
            throw ORMInvalidArgumentException::entityNotManaged($entity);
930
        }
931
932
        // skip if change tracking is "NOTIFY"
933 16
        if ($class->isChangeTrackingNotify()) {
934
            return;
935
        }
936
937 16
        if ( ! $class->isInheritanceTypeNone()) {
938 3
            $class = $this->em->getClassMetadata(get_class($entity));
939
        }
940
941 16
        $actualData = array();
942
943 16
        foreach ($class->reflFields as $name => $refProp) {
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
946 16
                && ! $class->isCollectionValuedAssociation($name)) {
947 16
                $actualData[$name] = $refProp->getValue($entity);
948
            }
949
        }
950
951 16
        if ( ! isset($this->originalEntityData[$oid])) {
952
            throw new \RuntimeException('Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.');
953
        }
954
955 16
        $originalData = $this->originalEntityData[$oid];
956 16
        $changeSet = array();
957
958 16
        foreach ($actualData as $propName => $actualValue) {
959 16
            $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
960
961 16
            if ($orgValue !== $actualValue) {
962 16
                $changeSet[$propName] = array($orgValue, $actualValue);
963
            }
964
        }
965
966 16
        if ($changeSet) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $changeSet of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
967 7
            if (isset($this->entityChangeSets[$oid])) {
968 6
                $this->entityChangeSets[$oid] = array_merge($this->entityChangeSets[$oid], $changeSet);
969 1
            } else if ( ! isset($this->entityInsertions[$oid])) {
970 1
                $this->entityChangeSets[$oid] = $changeSet;
971 1
                $this->entityUpdates[$oid]    = $entity;
972
            }
973 7
            $this->originalEntityData[$oid] = $actualData;
974
        }
975 16
    }
976
977
    /**
978
     * Executes all entity insertions for entities of the specified type.
979
     *
980
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
981
     *
982
     * @return void
983
     */
984 1008
    private function executeInserts($class)
985
    {
986 1008
        $entities   = array();
987 1008
        $className  = $class->name;
988 1008
        $persister  = $this->getEntityPersister($className);
989 1008
        $invoke     = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist);
990
991 1008
        foreach ($this->entityInsertions as $oid => $entity) {
992
993 1008
            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...
994 858
                continue;
995
            }
996
997 1008
            $persister->addInsert($entity);
998
999 1008
            unset($this->entityInsertions[$oid]);
1000
1001 1008
            if ($invoke !== ListenersInvoker::INVOKE_NONE) {
1002 1008
                $entities[] = $entity;
1003
            }
1004
        }
1005
1006 1008
        $postInsertIds = $persister->executeInserts();
1007
1008 1008
        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...
1009
            // Persister returned post-insert IDs
1010 921
            foreach ($postInsertIds as $postInsertId) {
1011 921
                $id      = $postInsertId['generatedId'];
1012 921
                $entity  = $postInsertId['entity'];
1013 921
                $oid     = spl_object_hash($entity);
1014 921
                $idField = $class->identifier[0];
1015
1016 921
                $class->reflFields[$idField]->setValue($entity, $id);
1017
1018 921
                $this->entityIdentifiers[$oid] = array($idField => $id);
1019 921
                $this->entityStates[$oid] = self::STATE_MANAGED;
1020 921
                $this->originalEntityData[$oid][$idField] = $id;
1021
1022 921
                $this->addToIdentityMap($entity);
1023
            }
1024
        }
1025
1026 1008
        foreach ($entities as $entity) {
1027 135
            $this->listenersInvoker->invoke($class, Events::postPersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
1028
        }
1029 1008
    }
1030
1031
    /**
1032
     * Executes all entity updates for entities of the specified type.
1033
     *
1034
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
1035
     *
1036
     * @return void
1037
     */
1038 116
    private function executeUpdates($class)
1039
    {
1040 116
        $className          = $class->name;
1041 116
        $persister          = $this->getEntityPersister($className);
1042 116
        $preUpdateInvoke    = $this->listenersInvoker->getSubscribedSystems($class, Events::preUpdate);
1043 116
        $postUpdateInvoke   = $this->listenersInvoker->getSubscribedSystems($class, Events::postUpdate);
1044
1045 116
        foreach ($this->entityUpdates as $oid => $entity) {
1046 116
            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...
1047 74
                continue;
1048
            }
1049
1050 116
            if ($preUpdateInvoke != ListenersInvoker::INVOKE_NONE) {
1051 13
                $this->listenersInvoker->invoke($class, Events::preUpdate, $entity, new PreUpdateEventArgs($entity, $this->em, $this->getEntityChangeSet($entity)), $preUpdateInvoke);
1052
1053 13
                $this->recomputeSingleEntityChangeSet($class, $entity);
1054
            }
1055
1056 116
            if ( ! empty($this->entityChangeSets[$oid])) {
1057 82
                $persister->update($entity);
1058
            }
1059
1060 112
            unset($this->entityUpdates[$oid]);
1061
1062 112
            if ($postUpdateInvoke != ListenersInvoker::INVOKE_NONE) {
1063 112
                $this->listenersInvoker->invoke($class, Events::postUpdate, $entity, new LifecycleEventArgs($entity, $this->em), $postUpdateInvoke);
1064
            }
1065
        }
1066 112
    }
1067
1068
    /**
1069
     * Executes all entity deletions for entities of the specified type.
1070
     *
1071
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
1072
     *
1073
     * @return void
1074
     */
1075 63
    private function executeDeletions($class)
1076
    {
1077 63
        $className  = $class->name;
1078 63
        $persister  = $this->getEntityPersister($className);
1079 63
        $invoke     = $this->listenersInvoker->getSubscribedSystems($class, Events::postRemove);
1080
1081 63
        foreach ($this->entityDeletions as $oid => $entity) {
1082 63
            if ($this->em->getClassMetadata(get_class($entity))->name !== $className) {
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1083 26
                continue;
1084
            }
1085
1086 63
            $persister->delete($entity);
1087
1088
            unset(
1089 63
                $this->entityDeletions[$oid],
1090 63
                $this->entityIdentifiers[$oid],
1091 63
                $this->originalEntityData[$oid],
1092 63
                $this->entityStates[$oid]
1093
            );
1094
1095
            // Entity with this $oid after deletion treated as NEW, even if the $oid
1096
            // is obtained by a new entity because the old one went out of scope.
1097
            //$this->entityStates[$oid] = self::STATE_NEW;
1098 63
            if ( ! $class->isIdentifierNatural()) {
1099 53
                $class->reflFields[$class->identifier[0]]->setValue($entity, null);
1100
            }
1101
1102 63
            if ($invoke !== ListenersInvoker::INVOKE_NONE) {
1103 63
                $this->listenersInvoker->invoke($class, Events::postRemove, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
1104
            }
1105
        }
1106 62
    }
1107
1108
    /**
1109
     * Gets the commit order.
1110
     *
1111
     * @param array|null $entityChangeSet
1112
     *
1113
     * @return array
1114
     */
1115 1012
    private function getCommitOrder(array $entityChangeSet = null)
1116
    {
1117 1012
        if ($entityChangeSet === null) {
1118 1012
            $entityChangeSet = array_merge($this->entityInsertions, $this->entityUpdates, $this->entityDeletions);
1119
        }
1120
1121 1012
        $calc = $this->getCommitOrderCalculator();
1122
1123
        // See if there are any new classes in the changeset, that are not in the
1124
        // commit order graph yet (don't have a node).
1125
        // We have to inspect changeSet to be able to correctly build dependencies.
1126
        // It is not possible to use IdentityMap here because post inserted ids
1127
        // are not yet available.
1128 1012
        $newNodes = array();
1129
1130 1012
        foreach ($entityChangeSet as $entity) {
1131 1012
            $class = $this->em->getClassMetadata(get_class($entity));
1132
1133 1012
            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...
1134 628
                continue;
1135
            }
1136
1137 1012
            $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...
1138
1139 1012
            $newNodes[] = $class;
1140
        }
1141
1142
        // Calculate dependencies for new nodes
1143 1012
        while ($class = array_pop($newNodes)) {
1144 1012
            foreach ($class->associationMappings as $assoc) {
1145 888
                if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) {
1146 848
                    continue;
1147
                }
1148
1149 841
                $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
1150
1151 841
                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...
1152 651
                    $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...
1153
1154 651
                    $newNodes[] = $targetClass;
1155
                }
1156
1157 841
                $joinColumns = reset($assoc['joinColumns']);
1158
1159 841
                $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...
1160
1161
                // If the target class has mapped subclasses, these share the same dependency.
1162 841
                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...
1163 834
                    continue;
1164
                }
1165
1166 217
                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...
1167 217
                    $targetSubClass = $this->em->getClassMetadata($subClassName);
1168
1169 217
                    if ( ! $calc->hasNode($subClassName)) {
1170 189
                        $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...
1171
1172 189
                        $newNodes[] = $targetSubClass;
1173
                    }
1174
1175 217
                    $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...
1176
                }
1177
            }
1178
        }
1179
1180 1012
        return $calc->sort();
1181
    }
1182
1183
    /**
1184
     * Schedules an entity for insertion into the database.
1185
     * If the entity already has an identifier, it will be added to the identity map.
1186
     *
1187
     * @param object $entity The entity to schedule for insertion.
1188
     *
1189
     * @return void
1190
     *
1191
     * @throws ORMInvalidArgumentException
1192
     * @throws \InvalidArgumentException
1193
     */
1194 1037
    public function scheduleForInsert($entity)
1195
    {
1196 1037
        $oid = spl_object_hash($entity);
1197
1198 1037
        if (isset($this->entityUpdates[$oid])) {
1199
            throw new InvalidArgumentException("Dirty entity can not be scheduled for insertion.");
1200
        }
1201
1202 1037
        if (isset($this->entityDeletions[$oid])) {
1203 1
            throw ORMInvalidArgumentException::scheduleInsertForRemovedEntity($entity);
1204
        }
1205 1037
        if (isset($this->originalEntityData[$oid]) && ! isset($this->entityInsertions[$oid])) {
1206 1
            throw ORMInvalidArgumentException::scheduleInsertForManagedEntity($entity);
1207
        }
1208
1209 1037
        if (isset($this->entityInsertions[$oid])) {
1210 1
            throw ORMInvalidArgumentException::scheduleInsertTwice($entity);
1211
        }
1212
1213 1037
        $this->entityInsertions[$oid] = $entity;
1214
1215 1037
        if (isset($this->entityIdentifiers[$oid])) {
1216 269
            $this->addToIdentityMap($entity);
1217
        }
1218
1219 1037
        if ($entity instanceof NotifyPropertyChanged) {
1220 5
            $entity->addPropertyChangedListener($this);
1221
        }
1222 1037
    }
1223
1224
    /**
1225
     * Checks whether an entity is scheduled for insertion.
1226
     *
1227
     * @param object $entity
1228
     *
1229
     * @return boolean
1230
     */
1231 631
    public function isScheduledForInsert($entity)
1232
    {
1233 631
        return isset($this->entityInsertions[spl_object_hash($entity)]);
1234
    }
1235
1236
    /**
1237
     * Schedules an entity for being updated.
1238
     *
1239
     * @param object $entity The entity to schedule for being updated.
1240
     *
1241
     * @return void
1242
     *
1243
     * @throws ORMInvalidArgumentException
1244
     */
1245 1
    public function scheduleForUpdate($entity)
1246
    {
1247 1
        $oid = spl_object_hash($entity);
1248
1249 1
        if ( ! isset($this->entityIdentifiers[$oid])) {
1250
            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "scheduling for update");
1251
        }
1252
1253 1
        if (isset($this->entityDeletions[$oid])) {
1254
            throw ORMInvalidArgumentException::entityIsRemoved($entity, "schedule for update");
1255
        }
1256
1257 1
        if ( ! isset($this->entityUpdates[$oid]) && ! isset($this->entityInsertions[$oid])) {
1258 1
            $this->entityUpdates[$oid] = $entity;
1259
        }
1260 1
    }
1261
1262
    /**
1263
     * INTERNAL:
1264
     * Schedules an extra update that will be executed immediately after the
1265
     * regular entity updates within the currently running commit cycle.
1266
     *
1267
     * Extra updates for entities are stored as (entity, changeset) tuples.
1268
     *
1269
     * @ignore
1270
     *
1271
     * @param object $entity    The entity for which to schedule an extra update.
1272
     * @param array  $changeset The changeset of the entity (what to update).
1273
     *
1274
     * @return void
1275
     */
1276 40
    public function scheduleExtraUpdate($entity, array $changeset)
1277
    {
1278 40
        $oid         = spl_object_hash($entity);
1279 40
        $extraUpdate = array($entity, $changeset);
1280
1281 40
        if (isset($this->extraUpdates[$oid])) {
1282 1
            list(, $changeset2) = $this->extraUpdates[$oid];
1283
1284 1
            $extraUpdate = array($entity, $changeset + $changeset2);
1285
        }
1286
1287 40
        $this->extraUpdates[$oid] = $extraUpdate;
1288 40
    }
1289
1290
    /**
1291
     * Checks whether an entity is registered as dirty in the unit of work.
1292
     * Note: Is not very useful currently as dirty entities are only registered
1293
     * at commit time.
1294
     *
1295
     * @param object $entity
1296
     *
1297
     * @return boolean
1298
     */
1299
    public function isScheduledForUpdate($entity)
1300
    {
1301
        return isset($this->entityUpdates[spl_object_hash($entity)]);
1302
    }
1303
1304
    /**
1305
     * Checks whether an entity is registered to be checked in the unit of work.
1306
     *
1307
     * @param object $entity
1308
     *
1309
     * @return boolean
1310
     */
1311 1
    public function isScheduledForDirtyCheck($entity)
1312
    {
1313 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...
1314
1315 1
        return isset($this->scheduledForSynchronization[$rootEntityName][spl_object_hash($entity)]);
1316
    }
1317
1318
    /**
1319
     * INTERNAL:
1320
     * Schedules an entity for deletion.
1321
     *
1322
     * @param object $entity
1323
     *
1324
     * @return void
1325
     */
1326 66
    public function scheduleForDelete($entity)
1327
    {
1328 66
        $oid = spl_object_hash($entity);
1329
1330 66
        if (isset($this->entityInsertions[$oid])) {
1331 1
            if ($this->isInIdentityMap($entity)) {
1332
                $this->removeFromIdentityMap($entity);
1333
            }
1334
1335 1
            unset($this->entityInsertions[$oid], $this->entityStates[$oid]);
1336
1337 1
            return; // entity has not been persisted yet, so nothing more to do.
1338
        }
1339
1340 66
        if ( ! $this->isInIdentityMap($entity)) {
1341 1
            return;
1342
        }
1343
1344 65
        $this->removeFromIdentityMap($entity);
1345
1346 65
        unset($this->entityUpdates[$oid]);
1347
1348 65
        if ( ! isset($this->entityDeletions[$oid])) {
1349 65
            $this->entityDeletions[$oid] = $entity;
1350 65
            $this->entityStates[$oid]    = self::STATE_REMOVED;
1351
        }
1352 65
    }
1353
1354
    /**
1355
     * Checks whether an entity is registered as removed/deleted with the unit
1356
     * of work.
1357
     *
1358
     * @param object $entity
1359
     *
1360
     * @return boolean
1361
     */
1362 17
    public function isScheduledForDelete($entity)
1363
    {
1364 17
        return isset($this->entityDeletions[spl_object_hash($entity)]);
1365
    }
1366
1367
    /**
1368
     * Checks whether an entity is scheduled for insertion, update or deletion.
1369
     *
1370
     * @param object $entity
1371
     *
1372
     * @return boolean
1373
     */
1374
    public function isEntityScheduled($entity)
1375
    {
1376
        $oid = spl_object_hash($entity);
1377
1378
        return isset($this->entityInsertions[$oid])
1379
            || isset($this->entityUpdates[$oid])
1380
            || isset($this->entityDeletions[$oid]);
1381
    }
1382
1383
    /**
1384
     * INTERNAL:
1385
     * Registers an entity in the identity map.
1386
     * Note that entities in a hierarchy are registered with the class name of
1387
     * the root entity.
1388
     *
1389
     * @ignore
1390
     *
1391
     * @param object $entity The entity to register.
1392
     *
1393
     * @return boolean TRUE if the registration was successful, FALSE if the identity of
1394
     *                 the entity in question is already managed.
1395
     *
1396
     * @throws ORMInvalidArgumentException
1397
     */
1398 1102
    public function addToIdentityMap($entity)
1399
    {
1400 1102
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1401 1102
        $identifier    = $this->entityIdentifiers[spl_object_hash($entity)];
1402
1403 1102
        if (empty($identifier) || in_array(null, $identifier, true)) {
1404 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...
1405
        }
1406
1407 1096
        $idHash    = implode(' ', $identifier);
1408 1096
        $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...
1409
1410 1096
        if (isset($this->identityMap[$className][$idHash])) {
1411 83
            return false;
1412
        }
1413
1414 1096
        $this->identityMap[$className][$idHash] = $entity;
1415
1416 1096
        return true;
1417
    }
1418
1419
    /**
1420
     * Gets the state of an entity with regard to the current unit of work.
1421
     *
1422
     * @param object   $entity
1423
     * @param int|null $assume The state to assume if the state is not yet known (not MANAGED or REMOVED).
1424
     *                         This parameter can be set to improve performance of entity state detection
1425
     *                         by potentially avoiding a database lookup if the distinction between NEW and DETACHED
1426
     *                         is either known or does not matter for the caller of the method.
1427
     *
1428
     * @return int The entity state.
1429
     */
1430 1050
    public function getEntityState($entity, $assume = null)
1431
    {
1432 1050
        $oid = spl_object_hash($entity);
1433
1434 1050
        if (isset($this->entityStates[$oid])) {
1435 784
            return $this->entityStates[$oid];
1436
        }
1437
1438 1044
        if ($assume !== null) {
1439 1040
            return $assume;
1440
        }
1441
1442
        // State can only be NEW or DETACHED, because MANAGED/REMOVED states are known.
1443
        // Note that you can not remember the NEW or DETACHED state in _entityStates since
1444
        // the UoW does not hold references to such objects and the object hash can be reused.
1445
        // More generally because the state may "change" between NEW/DETACHED without the UoW being aware of it.
1446 13
        $class = $this->em->getClassMetadata(get_class($entity));
1447 13
        $id    = $class->getIdentifierValues($entity);
1448
1449 13
        if ( ! $id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1454 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...
1455
        }
1456
1457
        switch (true) {
1458 10
            case ($class->isIdentifierNatural()):
0 ignored issues
show
Bug introduced by
The method isIdentifierNatural() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. Did you maybe mean isIdentifier()?

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

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1461 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...
1462
                        ? self::STATE_DETACHED
1463 1
                        : self::STATE_NEW;
1464
                }
1465
1466
                // Last try before db lookup: check the identity map.
1467 4
                if ($this->tryGetById($id, $class->rootEntityName)) {
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1473
                    return self::STATE_DETACHED;
1474
                }
1475
1476 4
                return self::STATE_NEW;
1477
1478 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...
1479
                // if we have a pre insert generator we can't be sure that having an id
1480
                // really means that the entity exists. We have to verify this through
1481
                // the last resort: a db lookup
1482
1483
                // Last try before db lookup: check the identity map.
1484
                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...
1485
                    return self::STATE_DETACHED;
1486
                }
1487
1488
                // db lookup
1489
                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...
1490
                    return self::STATE_DETACHED;
1491
                }
1492
1493
                return self::STATE_NEW;
1494
1495
            default:
1496 5
                return self::STATE_DETACHED;
1497
        }
1498
    }
1499
1500
    /**
1501
     * INTERNAL:
1502
     * Removes an entity from the identity map. This effectively detaches the
1503
     * entity from the persistence management of Doctrine.
1504
     *
1505
     * @ignore
1506
     *
1507
     * @param object $entity
1508
     *
1509
     * @return boolean
1510
     *
1511
     * @throws ORMInvalidArgumentException
1512
     */
1513 77
    public function removeFromIdentityMap($entity)
1514
    {
1515 77
        $oid           = spl_object_hash($entity);
1516 77
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1517 77
        $idHash        = implode(' ', $this->entityIdentifiers[$oid]);
1518
1519 77
        if ($idHash === '') {
1520
            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "remove from identity map");
1521
        }
1522
1523 77
        $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...
1524
1525 77
        if (isset($this->identityMap[$className][$idHash])) {
1526 77
            unset($this->identityMap[$className][$idHash]);
1527 77
            unset($this->readOnlyObjects[$oid]);
1528
1529
            //$this->entityStates[$oid] = self::STATE_DETACHED;
1530
1531 77
            return true;
1532
        }
1533
1534
        return false;
1535
    }
1536
1537
    /**
1538
     * INTERNAL:
1539
     * Gets an entity in the identity map by its identifier hash.
1540
     *
1541
     * @ignore
1542
     *
1543
     * @param string $idHash
1544
     * @param string $rootClassName
1545
     *
1546
     * @return object
1547
     */
1548 6
    public function getByIdHash($idHash, $rootClassName)
1549
    {
1550 6
        return $this->identityMap[$rootClassName][$idHash];
1551
    }
1552
1553
    /**
1554
     * INTERNAL:
1555
     * Tries to get an entity by its identifier hash. If no entity is found for
1556
     * the given hash, FALSE is returned.
1557
     *
1558
     * @ignore
1559
     *
1560
     * @param mixed  $idHash        (must be possible to cast it to string)
1561
     * @param string $rootClassName
1562
     *
1563
     * @return object|bool The found entity or FALSE.
1564
     */
1565 34
    public function tryGetByIdHash($idHash, $rootClassName)
1566
    {
1567 34
        $stringIdHash = (string) $idHash;
1568
1569 34
        return isset($this->identityMap[$rootClassName][$stringIdHash])
1570 34
            ? $this->identityMap[$rootClassName][$stringIdHash]
1571 34
            : false;
1572
    }
1573
1574
    /**
1575
     * Checks whether an entity is registered in the identity map of this UnitOfWork.
1576
     *
1577
     * @param object $entity
1578
     *
1579
     * @return boolean
1580
     */
1581 214
    public function isInIdentityMap($entity)
1582
    {
1583 214
        $oid = spl_object_hash($entity);
1584
1585 214
        if (empty($this->entityIdentifiers[$oid])) {
1586 31
            return false;
1587
        }
1588
1589 199
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1590 199
        $idHash        = implode(' ', $this->entityIdentifiers[$oid]);
1591
1592 199
        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...
1593
    }
1594
1595
    /**
1596
     * INTERNAL:
1597
     * Checks whether an identifier hash exists in the identity map.
1598
     *
1599
     * @ignore
1600
     *
1601
     * @param string $idHash
1602
     * @param string $rootClassName
1603
     *
1604
     * @return boolean
1605
     */
1606
    public function containsIdHash($idHash, $rootClassName)
1607
    {
1608
        return isset($this->identityMap[$rootClassName][$idHash]);
1609
    }
1610
1611
    /**
1612
     * Persists an entity as part of the current unit of work.
1613
     *
1614
     * @param object $entity The entity to persist.
1615
     *
1616
     * @return void
1617
     */
1618 1033
    public function persist($entity)
1619
    {
1620 1033
        $visited = array();
1621
1622 1033
        $this->doPersist($entity, $visited);
1623 1026
    }
1624
1625
    /**
1626
     * Persists an entity as part of the current unit of work.
1627
     *
1628
     * This method is internally called during persist() cascades as it tracks
1629
     * the already visited entities to prevent infinite recursions.
1630
     *
1631
     * @param object $entity  The entity to persist.
1632
     * @param array  $visited The already visited entities.
1633
     *
1634
     * @return void
1635
     *
1636
     * @throws ORMInvalidArgumentException
1637
     * @throws UnexpectedValueException
1638
     */
1639 1033
    private function doPersist($entity, array &$visited)
1640
    {
1641 1033
        $oid = spl_object_hash($entity);
1642
1643 1033
        if (isset($visited[$oid])) {
1644 109
            return; // Prevent infinite recursion
1645
        }
1646
1647 1033
        $visited[$oid] = $entity; // Mark visited
1648
1649 1033
        $class = $this->em->getClassMetadata(get_class($entity));
1650
1651
        // We assume NEW, so DETACHED entities result in an exception on flush (constraint violation).
1652
        // If we would detect DETACHED here we would throw an exception anyway with the same
1653
        // consequences (not recoverable/programming error), so just assuming NEW here
1654
        // lets us avoid some database lookups for entities with natural identifiers.
1655 1033
        $entityState = $this->getEntityState($entity, self::STATE_NEW);
1656
1657
        switch ($entityState) {
1658 1033
            case self::STATE_MANAGED:
1659
                // Nothing to do, except if policy is "deferred explicit"
1660 234
                if ($class->isChangeTrackingDeferredExplicit()) {
1661 2
                    $this->scheduleForDirtyCheck($entity);
1662
                }
1663 234
                break;
1664
1665 1033
            case self::STATE_NEW:
1666 1032
                $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...
1667 1032
                break;
1668
1669 1
            case self::STATE_REMOVED:
1670
                // Entity becomes managed again
1671 1
                unset($this->entityDeletions[$oid]);
1672 1
                $this->addToIdentityMap($entity);
1673
1674 1
                $this->entityStates[$oid] = self::STATE_MANAGED;
1675 1
                break;
1676
1677
            case self::STATE_DETACHED:
1678
                // Can actually not happen right now since we assume STATE_NEW.
1679
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "persisted");
1680
1681
            default:
1682
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1683
        }
1684
1685 1033
        $this->cascadePersist($entity, $visited);
1686 1026
    }
1687
1688
    /**
1689
     * Deletes an entity as part of the current unit of work.
1690
     *
1691
     * @param object $entity The entity to remove.
1692
     *
1693
     * @return void
1694
     */
1695 65
    public function remove($entity)
1696
    {
1697 65
        $visited = array();
1698
1699 65
        $this->doRemove($entity, $visited);
1700 65
    }
1701
1702
    /**
1703
     * Deletes an entity as part of the current unit of work.
1704
     *
1705
     * This method is internally called during delete() cascades as it tracks
1706
     * the already visited entities to prevent infinite recursions.
1707
     *
1708
     * @param object $entity  The entity to delete.
1709
     * @param array  $visited The map of the already visited entities.
1710
     *
1711
     * @return void
1712
     *
1713
     * @throws ORMInvalidArgumentException If the instance is a detached entity.
1714
     * @throws UnexpectedValueException
1715
     */
1716 65
    private function doRemove($entity, array &$visited)
1717
    {
1718 65
        $oid = spl_object_hash($entity);
1719
1720 65
        if (isset($visited[$oid])) {
1721 1
            return; // Prevent infinite recursion
1722
        }
1723
1724 65
        $visited[$oid] = $entity; // mark visited
1725
1726
        // Cascade first, because scheduleForDelete() removes the entity from the identity map, which
1727
        // can cause problems when a lazy proxy has to be initialized for the cascade operation.
1728 65
        $this->cascadeRemove($entity, $visited);
1729
1730 65
        $class       = $this->em->getClassMetadata(get_class($entity));
1731 65
        $entityState = $this->getEntityState($entity);
1732
1733
        switch ($entityState) {
1734 65
            case self::STATE_NEW:
1735 65
            case self::STATE_REMOVED:
1736
                // nothing to do
1737 2
                break;
1738
1739 65
            case self::STATE_MANAGED:
1740 65
                $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preRemove);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

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

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

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

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

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

Loading history...
1744
                }
1745
1746 65
                $this->scheduleForDelete($entity);
1747 65
                break;
1748
1749
            case self::STATE_DETACHED:
1750
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "removed");
1751
            default:
1752
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1753
        }
1754
1755 65
    }
1756
1757
    /**
1758
     * Merges the state of the given detached entity into this UnitOfWork.
1759
     *
1760
     * @param object $entity
1761
     *
1762
     * @return object The managed copy of the entity.
1763
     *
1764
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1765
     *         attribute and the version check against the managed copy fails.
1766
     *
1767
     * @todo Require active transaction!? OptimisticLockException may result in undefined state!?
1768
     */
1769 41
    public function merge($entity)
1770
    {
1771 41
        $visited = array();
1772
1773 41
        return $this->doMerge($entity, $visited);
1774
    }
1775
1776
    /**
1777
     * Executes a merge operation on an entity.
1778
     *
1779
     * @param object      $entity
1780
     * @param array       $visited
1781
     * @param object|null $prevManagedCopy
1782
     * @param array|null  $assoc
1783
     *
1784
     * @return object The managed copy of the entity.
1785
     *
1786
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1787
     *         attribute and the version check against the managed copy fails.
1788
     * @throws ORMInvalidArgumentException If the entity instance is NEW.
1789
     * @throws EntityNotFoundException
1790
     */
1791 41
    private function doMerge($entity, array &$visited, $prevManagedCopy = null, array $assoc = [])
1792
    {
1793 41
        $oid = spl_object_hash($entity);
1794
1795 41
        if (isset($visited[$oid])) {
1796 4
            $managedCopy = $visited[$oid];
1797
1798 4
            if ($prevManagedCopy !== null) {
1799 4
                $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy);
1800
            }
1801
1802 4
            return $managedCopy;
1803
        }
1804
1805 41
        $class = $this->em->getClassMetadata(get_class($entity));
1806
1807
        // First we assume DETACHED, although it can still be NEW but we can avoid
1808
        // an extra db-roundtrip this way. If it is not MANAGED but has an identity,
1809
        // we need to fetch it from the db anyway in order to merge.
1810
        // MANAGED entities are ignored by the merge operation.
1811 41
        $managedCopy = $entity;
1812
1813 41
        if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) {
1814
            // Try to look the entity up in the identity map.
1815 40
            $id = $class->getIdentifierValues($entity);
1816
1817
            // If there is no ID, it is actually NEW.
1818 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...
1819 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...
1820
1821 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...
1822
            } else {
1823 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...
1824 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...
1825 35
                    : $id;
1826
1827 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 1891 which is incompatible with the return type documented by Doctrine\ORM\UnitOfWork::doMerge of type object.
Loading history...
1828
1829 35
                if ($managedCopy) {
1830
                    // We have the entity in-memory already, just make sure its not removed.
1831 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 1827 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...
1832 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 1827 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...
1833
                    }
1834
                } else {
1835
                    // We need to fetch the managed copy in order to merge.
1836 24
                    $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...
1837
                }
1838
1839 35
                if ($managedCopy === null) {
1840
                    // If the identifier is ASSIGNED, it is NEW, otherwise an error
1841
                    // since the managed entity was not found.
1842 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...
1843 1
                        throw EntityNotFoundException::fromClassNameAndIdentifier(
1844 1
                            $class->getName(),
1845 1
                            $this->identifierFlattener->flattenIdentifier($class, $id)
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

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

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

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

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

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

Loading history...
1850 1
                    $class->setIdentifierValues($managedCopy, $id);
1851
1852 1
                    $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
                }
1854
            }
1855
1856 39
            if ($class->isVersioned && $this->isLoaded($managedCopy) && $this->isLoaded($entity)) {
0 ignored issues
show
Bug introduced by
Accessing isVersioned on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1827 can also be of type boolean; however, Doctrine\ORM\UnitOfWork::isLoaded() does only seem to accept object, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
1857 4
                $reflField          = $class->reflFields[$class->versionField];
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1858 4
                $managedCopyVersion = $reflField->getValue($managedCopy);
1859 4
                $entityVersion      = $reflField->getValue($entity);
1860
1861
                // Throw exception if versions don't match.
1862 4
                if ($managedCopyVersion != $entityVersion) {
1863 1
                    throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion);
1864
                }
1865
            }
1866
1867 38
            $visited[$oid] = $managedCopy; // mark visited
1868
1869 38
            if ($this->isLoaded($entity)) {
1870 31
                if ($managedCopy instanceof Proxy && ! $managedCopy->__isInitialized()) {
1871 4
                    $managedCopy->__load();
1872
                }
1873
1874 31
                $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy);
0 ignored issues
show
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1827 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...
1875
            }
1876
1877 38
            if ($class->isChangeTrackingDeferredExplicit()) {
1878
                $this->scheduleForDirtyCheck($entity);
1879
            }
1880
        }
1881
1882 39
        if ($prevManagedCopy !== null) {
1883 6
            $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy);
0 ignored issues
show
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1827 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...
1884
        }
1885
1886
        // Mark the managed copy visited as well
1887 39
        $visited[spl_object_hash($managedCopy)] = $managedCopy;
1888
1889 39
        $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 1827 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...
1890
1891 39
        return $managedCopy;
1892
    }
1893
1894
    /**
1895
     * Tests if an entity is loaded - must either be a loaded proxy or not a proxy
1896
     *
1897
     * @param object $entity
1898
     *
1899
     * @return bool
1900
     */
1901 39
    private function isLoaded($entity)
1902
    {
1903 39
        return !($entity instanceof Proxy) || $entity->__isInitialized();
1904
    }
1905
1906
    /**
1907
     * Sets/adds associated managed copies into the previous entity's association field
1908
     *
1909
     * @param object $entity
1910
     * @param array  $association
1911
     * @param object $previousManagedCopy
1912
     * @param object $managedCopy
1913
     *
1914
     * @return void
1915
     */
1916 6
    private function updateAssociationWithMergedEntity($entity, array $association, $previousManagedCopy, $managedCopy)
1917
    {
1918 6
        $assocField = $association['fieldName'];
1919 6
        $prevClass  = $this->em->getClassMetadata(get_class($previousManagedCopy));
1920
1921 6
        if ($association['type'] & ClassMetadata::TO_ONE) {
1922 6
            $prevClass->reflFields[$assocField]->setValue($previousManagedCopy, $managedCopy);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1934
        }
1935 1
    }
1936
1937
    /**
1938
     * Detaches an entity from the persistence management. It's persistence will
1939
     * no longer be managed by Doctrine.
1940
     *
1941
     * @param object $entity The entity to detach.
1942
     *
1943
     * @return void
1944
     */
1945 12
    public function detach($entity)
1946
    {
1947 12
        $visited = array();
1948
1949 12
        $this->doDetach($entity, $visited);
1950 12
    }
1951
1952
    /**
1953
     * Executes a detach operation on the given entity.
1954
     *
1955
     * @param object  $entity
1956
     * @param array   $visited
1957
     * @param boolean $noCascade if true, don't cascade detach operation.
1958
     *
1959
     * @return void
1960
     */
1961 15
    private function doDetach($entity, array &$visited, $noCascade = false)
1962
    {
1963 15
        $oid = spl_object_hash($entity);
1964
1965 15
        if (isset($visited[$oid])) {
1966
            return; // Prevent infinite recursion
1967
        }
1968
1969 15
        $visited[$oid] = $entity; // mark visited
1970
1971 15
        switch ($this->getEntityState($entity, self::STATE_DETACHED)) {
1972 15
            case self::STATE_MANAGED:
1973 13
                if ($this->isInIdentityMap($entity)) {
1974 12
                    $this->removeFromIdentityMap($entity);
1975
                }
1976
1977
                unset(
1978 13
                    $this->entityInsertions[$oid],
1979 13
                    $this->entityUpdates[$oid],
1980 13
                    $this->entityDeletions[$oid],
1981 13
                    $this->entityIdentifiers[$oid],
1982 13
                    $this->entityStates[$oid],
1983 13
                    $this->originalEntityData[$oid]
1984
                );
1985 13
                break;
1986 3
            case self::STATE_NEW:
1987 3
            case self::STATE_DETACHED:
1988 3
                return;
1989
        }
1990
1991 13
        if ( ! $noCascade) {
1992 13
            $this->cascadeDetach($entity, $visited);
1993
        }
1994 13
    }
1995
1996
    /**
1997
     * Refreshes the state of the given entity from the database, overwriting
1998
     * any local, unpersisted changes.
1999
     *
2000
     * @param object $entity The entity to refresh.
2001
     *
2002
     * @return void
2003
     *
2004
     * @throws InvalidArgumentException If the entity is not MANAGED.
2005
     */
2006 17
    public function refresh($entity)
2007
    {
2008 17
        $visited = array();
2009
2010 17
        $this->doRefresh($entity, $visited);
2011 17
    }
2012
2013
    /**
2014
     * Executes a refresh operation on an entity.
2015
     *
2016
     * @param object $entity  The entity to refresh.
2017
     * @param array  $visited The already visited entities during cascades.
2018
     *
2019
     * @return void
2020
     *
2021
     * @throws ORMInvalidArgumentException If the entity is not MANAGED.
2022
     */
2023 17
    private function doRefresh($entity, array &$visited)
2024
    {
2025 17
        $oid = spl_object_hash($entity);
2026
2027 17
        if (isset($visited[$oid])) {
2028
            return; // Prevent infinite recursion
2029
        }
2030
2031 17
        $visited[$oid] = $entity; // mark visited
2032
2033 17
        $class = $this->em->getClassMetadata(get_class($entity));
2034
2035 17
        if ($this->getEntityState($entity) !== self::STATE_MANAGED) {
2036
            throw ORMInvalidArgumentException::entityNotManaged($entity);
2037
        }
2038
2039 17
        $this->getEntityPersister($class->name)->refresh(
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2040 17
            array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
2041
            $entity
2042
        );
2043
2044 17
        $this->cascadeRefresh($entity, $visited);
2045 17
    }
2046
2047
    /**
2048
     * Cascades a refresh operation to associated entities.
2049
     *
2050
     * @param object $entity
2051
     * @param array  $visited
2052
     *
2053
     * @return void
2054
     */
2055 17
    private function cascadeRefresh($entity, array &$visited)
2056
    {
2057 17
        $class = $this->em->getClassMetadata(get_class($entity));
2058
2059 17
        $associationMappings = array_filter(
2060 17
            $class->associationMappings,
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2066
2067
            switch (true) {
2068 5
                case ($relatedEntities instanceof PersistentCollection):
2069
                    // Unwrap so that foreach() does not initialize
2070 5
                    $relatedEntities = $relatedEntities->unwrap();
2071
                    // break; is commented intentionally!
2072
2073
                case ($relatedEntities instanceof Collection):
2074
                case (is_array($relatedEntities)):
2075 5
                    foreach ($relatedEntities as $relatedEntity) {
2076
                        $this->doRefresh($relatedEntity, $visited);
2077
                    }
2078 5
                    break;
2079
2080
                case ($relatedEntities !== null):
2081
                    $this->doRefresh($relatedEntities, $visited);
2082
                    break;
2083
2084 5
                default:
2085
                    // Do nothing
2086
            }
2087
        }
2088 17
    }
2089
2090
    /**
2091
     * Cascades a detach operation to associated entities.
2092
     *
2093
     * @param object $entity
2094
     * @param array  $visited
2095
     *
2096
     * @return void
2097
     */
2098 13
    private function cascadeDetach($entity, array &$visited)
2099
    {
2100 13
        $class = $this->em->getClassMetadata(get_class($entity));
2101
2102 13
        $associationMappings = array_filter(
2103 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...
2104
            function ($assoc) { return $assoc['isCascadeDetach']; }
2105
        );
2106
2107 13
        foreach ($associationMappings as $assoc) {
2108 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...
2109
2110
            switch (true) {
2111 3
                case ($relatedEntities instanceof PersistentCollection):
2112
                    // Unwrap so that foreach() does not initialize
2113 2
                    $relatedEntities = $relatedEntities->unwrap();
2114
                    // break; is commented intentionally!
2115
2116
                case ($relatedEntities instanceof Collection):
2117
                case (is_array($relatedEntities)):
2118 3
                    foreach ($relatedEntities as $relatedEntity) {
2119 1
                        $this->doDetach($relatedEntity, $visited);
2120
                    }
2121 3
                    break;
2122
2123
                case ($relatedEntities !== null):
2124
                    $this->doDetach($relatedEntities, $visited);
2125
                    break;
2126
2127 3
                default:
2128
                    // Do nothing
2129
            }
2130
        }
2131 13
    }
2132
2133
    /**
2134
     * Cascades a merge operation to associated entities.
2135
     *
2136
     * @param object $entity
2137
     * @param object $managedCopy
2138
     * @param array  $visited
2139
     *
2140
     * @return void
2141
     */
2142 39
    private function cascadeMerge($entity, $managedCopy, array &$visited)
2143
    {
2144 39
        $class = $this->em->getClassMetadata(get_class($entity));
2145
2146 39
        $associationMappings = array_filter(
2147 39
            $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...
2148
            function ($assoc) { return $assoc['isCascadeMerge']; }
2149
        );
2150
2151 39
        foreach ($associationMappings as $assoc) {
2152 16
            $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
0 ignored issues
show
Bug introduced by
Accessing reflFields on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2156 1
                    continue;
2157
                }
2158
2159 9
                if ($relatedEntities instanceof PersistentCollection) {
2160
                    // Unwrap so that foreach() does not initialize
2161 5
                    $relatedEntities = $relatedEntities->unwrap();
2162
                }
2163
2164 9
                foreach ($relatedEntities as $relatedEntity) {
2165 9
                    $this->doMerge($relatedEntity, $visited, $managedCopy, $assoc);
2166
                }
2167 7
            } else if ($relatedEntities !== null) {
2168 15
                $this->doMerge($relatedEntities, $visited, $managedCopy, $assoc);
2169
            }
2170
        }
2171 39
    }
2172
2173
    /**
2174
     * Cascades the save operation to associated entities.
2175
     *
2176
     * @param object $entity
2177
     * @param array  $visited
2178
     *
2179
     * @return void
2180
     */
2181 1033
    private function cascadePersist($entity, array &$visited)
2182
    {
2183 1033
        $class = $this->em->getClassMetadata(get_class($entity));
2184
2185 1033
        $associationMappings = array_filter(
2186 1033
            $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...
2187
            function ($assoc) { return $assoc['isCascadePersist']; }
2188
        );
2189
2190 1033
        foreach ($associationMappings as $assoc) {
2191 652
            $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...
2192
2193
            switch (true) {
2194 652
                case ($relatedEntities instanceof PersistentCollection):
2195
                    // Unwrap so that foreach() does not initialize
2196 21
                    $relatedEntities = $relatedEntities->unwrap();
2197
                    // break; is commented intentionally!
2198
2199
                case ($relatedEntities instanceof Collection):
2200 592
                case (is_array($relatedEntities)):
2201 556
                    if (($assoc['type'] & ClassMetadata::TO_MANY) <= 0) {
2202 3
                        throw ORMInvalidArgumentException::invalidAssociation(
2203 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...
2204
                            $assoc,
2205
                            $relatedEntities
2206
                        );
2207
                    }
2208
2209 553
                    foreach ($relatedEntities as $relatedEntity) {
2210 282
                        $this->doPersist($relatedEntity, $visited);
2211
                    }
2212
2213 553
                    break;
2214
2215 582
                case ($relatedEntities !== null):
2216 246
                    if (! $relatedEntities instanceof $assoc['targetEntity']) {
2217 4
                        throw ORMInvalidArgumentException::invalidAssociation(
2218 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...
2219
                            $assoc,
2220
                            $relatedEntities
2221
                        );
2222
                    }
2223
2224 242
                    $this->doPersist($relatedEntities, $visited);
2225 242
                    break;
2226
2227 646
                default:
2228
                    // Do nothing
2229
            }
2230
        }
2231 1026
    }
2232
2233
    /**
2234
     * Cascades the delete operation to associated entities.
2235
     *
2236
     * @param object $entity
2237
     * @param array  $visited
2238
     *
2239
     * @return void
2240
     */
2241 65
    private function cascadeRemove($entity, array &$visited)
2242
    {
2243 65
        $class = $this->em->getClassMetadata(get_class($entity));
2244
2245 65
        $associationMappings = array_filter(
2246 65
            $class->associationMappings,
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2258
2259
            switch (true) {
2260 26
                case ($relatedEntities instanceof Collection):
2261 19
                case (is_array($relatedEntities)):
2262
                    // If its a PersistentCollection initialization is intended! No unwrap!
2263 20
                    foreach ($relatedEntities as $relatedEntity) {
2264 10
                        $entitiesToCascade[] = $relatedEntity;
2265
                    }
2266 20
                    break;
2267
2268 19
                case ($relatedEntities !== null):
2269 7
                    $entitiesToCascade[] = $relatedEntities;
2270 7
                    break;
2271
2272 26
                default:
2273
                    // Do nothing
2274
            }
2275
        }
2276
2277 65
        foreach ($entitiesToCascade as $relatedEntity) {
2278 16
            $this->doRemove($relatedEntity, $visited);
2279
        }
2280 65
    }
2281
2282
    /**
2283
     * Acquire a lock on the given entity.
2284
     *
2285
     * @param object $entity
2286
     * @param int    $lockMode
2287
     * @param int    $lockVersion
2288
     *
2289
     * @return void
2290
     *
2291
     * @throws ORMInvalidArgumentException
2292
     * @throws TransactionRequiredException
2293
     * @throws OptimisticLockException
2294
     */
2295 11
    public function lock($entity, $lockMode, $lockVersion = null)
2296
    {
2297 11
        if ($entity === null) {
2298 1
            throw new \InvalidArgumentException("No entity passed to UnitOfWork#lock().");
2299
        }
2300
2301 10
        if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) {
2302 1
            throw ORMInvalidArgumentException::entityNotManaged($entity);
2303
        }
2304
2305 9
        $class = $this->em->getClassMetadata(get_class($entity));
2306
2307
        switch (true) {
2308 9
            case LockMode::OPTIMISTIC === $lockMode:
2309 6
                if ( ! $class->isVersioned) {
0 ignored issues
show
Bug introduced by
Accessing isVersioned on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2310 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...
2311
                }
2312
2313 4
                if ($lockVersion === null) {
2314
                    return;
2315
                }
2316
2317 4
                if ($entity instanceof Proxy && !$entity->__isInitialized__) {
0 ignored issues
show
Bug introduced by
Accessing __isInitialized__ on the interface Doctrine\ORM\Proxy\Proxy suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2322
2323 4
                if ($entityVersion != $lockVersion) {
2324 2
                    throw OptimisticLockException::lockFailedVersionMismatch($entity, $lockVersion, $entityVersion);
2325
                }
2326
2327 2
                break;
2328
2329 3
            case LockMode::NONE === $lockMode:
2330 3
            case LockMode::PESSIMISTIC_READ === $lockMode:
2331 1
            case LockMode::PESSIMISTIC_WRITE === $lockMode:
2332 3
                if (!$this->em->getConnection()->isTransactionActive()) {
2333 2
                    throw TransactionRequiredException::transactionRequired();
2334
                }
2335
2336 1
                $oid = spl_object_hash($entity);
2337
2338 1
                $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...
2339 1
                    array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
2340
                    $lockMode
2341
                );
2342 1
                break;
2343
2344
            default:
2345
                // Do nothing
2346
        }
2347 3
    }
2348
2349
    /**
2350
     * Gets the CommitOrderCalculator used by the UnitOfWork to order commits.
2351
     *
2352
     * @return \Doctrine\ORM\Internal\CommitOrderCalculator
2353
     */
2354 1012
    public function getCommitOrderCalculator()
2355
    {
2356 1012
        return new Internal\CommitOrderCalculator();
2357
    }
2358
2359
    /**
2360
     * Clears the UnitOfWork.
2361
     *
2362
     * @param string|null $entityName if given, only entities of this type will get detached.
2363
     *
2364
     * @return void
2365
     */
2366 1224
    public function clear($entityName = null)
2367
    {
2368 1224
        if ($entityName === null) {
2369 1223
            $this->identityMap =
2370 1223
            $this->entityIdentifiers =
2371 1223
            $this->originalEntityData =
2372 1223
            $this->entityChangeSets =
2373 1223
            $this->entityStates =
2374 1223
            $this->scheduledForSynchronization =
2375 1223
            $this->entityInsertions =
2376 1223
            $this->entityUpdates =
2377 1223
            $this->entityDeletions =
2378 1223
            $this->collectionDeletions =
2379 1223
            $this->collectionUpdates =
2380 1223
            $this->extraUpdates =
2381 1223
            $this->readOnlyObjects =
2382 1223
            $this->visitedCollections =
2383 1223
            $this->orphanRemovals = array();
2384
        } else {
2385 3
            $this->clearIdentityMapForEntityName($entityName);
2386 3
            $this->clearEntityInsertionsForEntityName($entityName);
2387
        }
2388
2389 1224
        if ($this->evm->hasListeners(Events::onClear)) {
2390 7
            $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em, $entityName));
2391
        }
2392 1224
    }
2393
2394
    /**
2395
     * INTERNAL:
2396
     * Schedules an orphaned entity for removal. The remove() operation will be
2397
     * invoked on that entity at the beginning of the next commit of this
2398
     * UnitOfWork.
2399
     *
2400
     * @ignore
2401
     *
2402
     * @param object $entity
2403
     *
2404
     * @return void
2405
     */
2406 17
    public function scheduleOrphanRemoval($entity)
2407
    {
2408 17
        $this->orphanRemovals[spl_object_hash($entity)] = $entity;
2409 17
    }
2410
2411
    /**
2412
     * INTERNAL:
2413
     * Cancels a previously scheduled orphan removal.
2414
     *
2415
     * @ignore
2416
     *
2417
     * @param object $entity
2418
     *
2419
     * @return void
2420
     */
2421 112
    public function cancelOrphanRemoval($entity)
2422
    {
2423 112
        unset($this->orphanRemovals[spl_object_hash($entity)]);
2424 112
    }
2425
2426
    /**
2427
     * INTERNAL:
2428
     * Schedules a complete collection for removal when this UnitOfWork commits.
2429
     *
2430
     * @param PersistentCollection $coll
2431
     *
2432
     * @return void
2433
     */
2434 13
    public function scheduleCollectionDeletion(PersistentCollection $coll)
2435
    {
2436 13
        $coid = spl_object_hash($coll);
2437
2438
        // TODO: if $coll is already scheduled for recreation ... what to do?
2439
        // Just remove $coll from the scheduled recreations?
2440 13
        unset($this->collectionUpdates[$coid]);
2441
2442 13
        $this->collectionDeletions[$coid] = $coll;
2443 13
    }
2444
2445
    /**
2446
     * @param PersistentCollection $coll
2447
     *
2448
     * @return bool
2449
     */
2450
    public function isCollectionScheduledForDeletion(PersistentCollection $coll)
2451
    {
2452
        return isset($this->collectionDeletions[spl_object_hash($coll)]);
2453
    }
2454
2455
    /**
2456
     * @param ClassMetadata $class
2457
     *
2458
     * @return \Doctrine\Common\Persistence\ObjectManagerAware|object
2459
     */
2460 674
    private function newInstance($class)
2461
    {
2462 674
        $entity = $class->newInstance();
2463
2464 674
        if ($entity instanceof \Doctrine\Common\Persistence\ObjectManagerAware) {
2465 4
            $entity->injectObjectManager($this->em, $class);
2466
        }
2467
2468 674
        return $entity;
2469
    }
2470
2471
    /**
2472
     * INTERNAL:
2473
     * Creates an entity. Used for reconstitution of persistent entities.
2474
     *
2475
     * Internal note: Highly performance-sensitive method.
2476
     *
2477
     * @ignore
2478
     *
2479
     * @param string $className The name of the entity class.
2480
     * @param array  $data      The data for the entity.
2481
     * @param array  $hints     Any hints to account for during reconstitution/lookup of the entity.
2482
     *
2483
     * @return object The managed entity instance.
2484
     *
2485
     * @todo Rename: getOrCreateEntity
2486
     */
2487 812
    public function createEntity($className, array $data, &$hints = array())
2488
    {
2489 812
        $class = $this->em->getClassMetadata($className);
2490
        //$isReadOnly = isset($hints[Query::HINT_READ_ONLY]);
2491
2492 812
        $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...
2493 812
        $idHash = implode(' ', $id);
2494
2495 812
        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...
2496 312
            $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...
2497 312
            $oid = spl_object_hash($entity);
2498
2499
            if (
2500 312
                isset($hints[Query::HINT_REFRESH])
2501 312
                && isset($hints[Query::HINT_REFRESH_ENTITY])
2502 312
                && ($unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY]) !== $entity
2503 312
                && $unmanagedProxy instanceof Proxy
2504 312
                && $this->isIdentifierEquals($unmanagedProxy, $entity)
2505
            ) {
2506
                // DDC-1238 - we have a managed instance, but it isn't the provided one.
2507
                // Therefore we clear its identifier. Also, we must re-fetch metadata since the
2508
                // refreshed object may be anything
2509
2510 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...
2511 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...
2512
                }
2513
2514 2
                return $unmanagedProxy;
2515
            }
2516
2517 310
            if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
2518 22
                $entity->__setInitialized(true);
2519
2520 22
                $overrideLocalValues = true;
2521
2522 22
                if ($entity instanceof NotifyPropertyChanged) {
2523 22
                    $entity->addPropertyChangedListener($this);
2524
                }
2525
            } else {
2526 290
                $overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
2527
2528
                // If only a specific entity is set to refresh, check that it's the one
2529 290
                if (isset($hints[Query::HINT_REFRESH_ENTITY])) {
2530 71
                    $overrideLocalValues = $hints[Query::HINT_REFRESH_ENTITY] === $entity;
2531
                }
2532
            }
2533
2534 310
            if ($overrideLocalValues) {
2535
                // inject ObjectManager upon refresh.
2536 113
                if ($entity instanceof ObjectManagerAware) {
2537 3
                    $entity->injectObjectManager($this->em, $class);
2538
                }
2539
2540 310
                $this->originalEntityData[$oid] = $data;
2541
            }
2542
        } else {
2543 670
            $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...
2544 670
            $oid    = spl_object_hash($entity);
2545
2546 670
            $this->entityIdentifiers[$oid]  = $id;
2547 670
            $this->entityStates[$oid]       = self::STATE_MANAGED;
2548 670
            $this->originalEntityData[$oid] = $data;
2549
2550 670
            $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...
2551
2552 670
            if ($entity instanceof NotifyPropertyChanged) {
2553 2
                $entity->addPropertyChangedListener($this);
2554
            }
2555
2556 670
            $overrideLocalValues = true;
2557
        }
2558
2559 811
        if ( ! $overrideLocalValues) {
2560 220
            return $entity;
2561
        }
2562
2563 708
        foreach ($data as $field => $value) {
2564 708
            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...
2565 708
                $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...
2566
            }
2567
        }
2568
2569
        // Loading the entity right here, if its in the eager loading map get rid of it there.
2570 708
        unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
2571
2572 708
        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...
2573
            unset($this->eagerLoadingEntities[$class->rootEntityName]);
2574
        }
2575
2576
        // Properly initialize any unfetched associations, if partial objects are not allowed.
2577 708
        if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
2578 33
            return $entity;
2579
        }
2580
2581 675
        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...
2582
            // Check if the association is not among the fetch-joined associations already.
2583 588
            if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) {
2584 261
                continue;
2585
            }
2586
2587 566
            $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
2588
2589
            switch (true) {
2590 566
                case ($assoc['type'] & ClassMetadata::TO_ONE):
2591 486
                    if ( ! $assoc['isOwningSide']) {
2592
2593
                        // use the given entity association
2594 64
                        if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2595
2596 2
                            $this->originalEntityData[$oid][$field] = $data[$field];
2597
2598 2
                            $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...
2599 2
                            $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...
2600
2601 2
                            continue 2;
2602
                        }
2603
2604
                        // Inverse side of x-to-one can never be lazy
2605 62
                        $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...
2606
2607 62
                        continue 2;
2608
                    }
2609
2610
                    // use the entity association
2611 486
                    if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2612 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...
2613 38
                        $this->originalEntityData[$oid][$field] = $data[$field];
2614
2615 38
                        continue;
2616
                    }
2617
2618 479
                    $associatedId = array();
2619
2620
                    // TODO: Is this even computed right in all cases of composite keys?
2621 479
                    foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
2622 479
                        $joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
2623
2624 479
                        if ($joinColumnValue !== null) {
2625 284
                            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...
2626 11
                                $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
2627
                            } else {
2628 284
                                $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...
2629
                            }
2630 287
                        } 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...
2631 287
                            && 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...
2632
                        ) {
2633
                            // the missing key is part of target's entity primary key
2634 7
                            $associatedId = array();
2635 479
                            break;
2636
                        }
2637
                    }
2638
2639 479
                    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...
2640
                        // Foreign key is NULL
2641 287
                        $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...
2642 287
                        $this->originalEntityData[$oid][$field] = null;
2643
2644 287
                        continue;
2645
                    }
2646
2647 284
                    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...
2648 281
                        $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...
2649
                    }
2650
2651
                    // Foreign key is set
2652
                    // Check identity map first
2653
                    // FIXME: Can break easily with composite keys if join column values are in
2654
                    //        wrong order. The correct order is the one in ClassMetadata#identifier.
2655 284
                    $relatedIdHash = implode(' ', $associatedId);
2656
2657
                    switch (true) {
2658 284
                        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...
2659 166
                            $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...
2660
2661
                            // If this is an uninitialized proxy, we are deferring eager loads,
2662
                            // this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
2663
                            // then we can append this entity for eager loading!
2664 166
                            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...
2665 166
                                isset($hints[self::HINT_DEFEREAGERLOAD]) &&
2666 166
                                !$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...
2667 166
                                $newValue instanceof Proxy &&
2668 166
                                $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...
2669
2670
                                $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...
2671
                            }
2672
2673 166
                            break;
2674
2675 191
                        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...
2676
                            // If it might be a subtype, it can not be lazy. There isn't even
2677
                            // a way to solve this with deferred eager loading, which means putting
2678
                            // an entity with subclasses at a *-to-one location is really bad! (performance-wise)
2679 30
                            $newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, $associatedId);
2680 30
                            break;
2681
2682
                        default:
2683
                            switch (true) {
2684
                                // We are negating the condition here. Other cases will assume it is valid!
2685 162
                                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...
2686 155
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2687 155
                                    break;
2688
2689
                                // Deferred eager load only works for single identifier classes
2690 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...
2691
                                    // TODO: Is there a faster approach?
2692 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...
2693
2694 7
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2695 7
                                    break;
2696
2697
                                default:
2698
                                    // TODO: This is very imperformant, ignore it?
2699
                                    $newValue = $this->em->find($assoc['targetEntity'], $associatedId);
2700
                                    break;
2701
                            }
2702
2703
                            // PERF: Inlined & optimized code from UnitOfWork#registerManaged()
2704 162
                            $newValueOid = spl_object_hash($newValue);
2705 162
                            $this->entityIdentifiers[$newValueOid] = $associatedId;
2706 162
                            $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...
2707
2708
                            if (
2709 162
                                $newValue instanceof NotifyPropertyChanged &&
2710 162
                                ( ! $newValue instanceof Proxy || $newValue->__isInitialized())
2711
                            ) {
2712
                                $newValue->addPropertyChangedListener($this);
2713
                            }
2714 162
                            $this->entityStates[$newValueOid] = self::STATE_MANAGED;
2715
                            // make sure that when an proxy is then finally loaded, $this->originalEntityData is set also!
2716 162
                            break;
2717
                    }
2718
2719 284
                    $this->originalEntityData[$oid][$field] = $newValue;
2720 284
                    $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...
2721
2722 284
                    if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
2723 50
                        $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...
2724 50
                        $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...
2725
                    }
2726
2727 284
                    break;
2728
2729
                default:
2730
                    // Ignore if its a cached collection
2731 488
                    if (isset($hints[Query::HINT_CACHE_ENABLED]) && $class->getFieldValue($entity, $field) instanceof PersistentCollection) {
2732
                        break;
2733
                    }
2734
2735
                    // use the given collection
2736 488
                    if (isset($data[$field]) && $data[$field] instanceof PersistentCollection) {
2737
2738 3
                        $data[$field]->setOwner($entity, $assoc);
2739
2740 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...
2741 3
                        $this->originalEntityData[$oid][$field] = $data[$field];
2742
2743 3
                        break;
2744
                    }
2745
2746
                    // Inject collection
2747 488
                    $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...
2748 488
                    $pColl->setOwner($entity, $assoc);
2749 488
                    $pColl->setInitialized(false);
2750
2751 488
                    $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...
2752 488
                    $reflField->setValue($entity, $pColl);
2753
2754 488
                    if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
2755 4
                        $this->loadCollection($pColl);
2756 4
                        $pColl->takeSnapshot();
2757
                    }
2758
2759 488
                    $this->originalEntityData[$oid][$field] = $pColl;
2760 566
                    break;
2761
            }
2762
        }
2763
2764 675
        if ($overrideLocalValues) {
2765
            // defer invoking of postLoad event to hydration complete step
2766 675
            $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...
2767
        }
2768
2769 675
        return $entity;
2770
    }
2771
2772
    /**
2773
     * @return void
2774
     */
2775 868
    public function triggerEagerLoads()
2776
    {
2777 868
        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...
2778 868
            return;
2779
        }
2780
2781
        // avoid infinite recursion
2782 7
        $eagerLoadingEntities       = $this->eagerLoadingEntities;
2783 7
        $this->eagerLoadingEntities = array();
2784
2785 7
        foreach ($eagerLoadingEntities as $entityName => $ids) {
2786 7
            if ( ! $ids) {
2787
                continue;
2788
            }
2789
2790 7
            $class = $this->em->getClassMetadata($entityName);
2791
2792 7
            $this->getEntityPersister($entityName)->loadAll(
2793 7
                array_combine($class->identifier, array(array_values($ids)))
0 ignored issues
show
Bug introduced by
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2794
            );
2795
        }
2796 7
    }
2797
2798
    /**
2799
     * Initializes (loads) an uninitialized persistent collection of an entity.
2800
     *
2801
     * @param \Doctrine\ORM\PersistentCollection $collection The collection to initialize.
2802
     *
2803
     * @return void
2804
     *
2805
     * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733.
2806
     */
2807 143
    public function loadCollection(PersistentCollection $collection)
2808
    {
2809 143
        $assoc     = $collection->getMapping();
2810 143
        $persister = $this->getEntityPersister($assoc['targetEntity']);
2811
2812 143
        switch ($assoc['type']) {
2813 143
            case ClassMetadata::ONE_TO_MANY:
2814 76
                $persister->loadOneToManyCollection($assoc, $collection->getOwner(), $collection);
2815 76
                break;
2816
2817 81
            case ClassMetadata::MANY_TO_MANY:
2818 81
                $persister->loadManyToManyCollection($assoc, $collection->getOwner(), $collection);
2819 81
                break;
2820
        }
2821
2822 143
        $collection->setInitialized(true);
2823 143
    }
2824
2825
    /**
2826
     * Gets the identity map of the UnitOfWork.
2827
     *
2828
     * @return array
2829
     */
2830 2
    public function getIdentityMap()
2831
    {
2832 2
        return $this->identityMap;
2833
    }
2834
2835
    /**
2836
     * Gets the original data of an entity. The original data is the data that was
2837
     * present at the time the entity was reconstituted from the database.
2838
     *
2839
     * @param object $entity
2840
     *
2841
     * @return array
2842
     */
2843 117
    public function getOriginalEntityData($entity)
2844
    {
2845 117
        $oid = spl_object_hash($entity);
2846
2847 117
        return isset($this->originalEntityData[$oid])
2848 113
            ? $this->originalEntityData[$oid]
2849 117
            : [];
2850
    }
2851
2852
    /**
2853
     * @ignore
2854
     *
2855
     * @param object $entity
2856
     * @param array  $data
2857
     *
2858
     * @return void
2859
     */
2860
    public function setOriginalEntityData($entity, array $data)
2861
    {
2862
        $this->originalEntityData[spl_object_hash($entity)] = $data;
2863
    }
2864
2865
    /**
2866
     * INTERNAL:
2867
     * Sets a property value of the original data array of an entity.
2868
     *
2869
     * @ignore
2870
     *
2871
     * @param string $oid
2872
     * @param string $property
2873
     * @param mixed  $value
2874
     *
2875
     * @return void
2876
     */
2877 314
    public function setOriginalEntityProperty($oid, $property, $value)
2878
    {
2879 314
        $this->originalEntityData[$oid][$property] = $value;
2880 314
    }
2881
2882
    /**
2883
     * Gets the identifier of an entity.
2884
     * The returned value is always an array of identifier values. If the entity
2885
     * has a composite identifier then the identifier values are in the same
2886
     * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames().
2887
     *
2888
     * @param object $entity
2889
     *
2890
     * @return array The identifier values.
2891
     */
2892 845
    public function getEntityIdentifier($entity)
2893
    {
2894 845
        return $this->entityIdentifiers[spl_object_hash($entity)];
2895
    }
2896
2897
    /**
2898
     * Processes an entity instance to extract their identifier values.
2899
     *
2900
     * @param object $entity The entity instance.
2901
     *
2902
     * @return mixed A scalar value.
2903
     *
2904
     * @throws \Doctrine\ORM\ORMInvalidArgumentException
2905
     */
2906 127
    public function getSingleIdentifierValue($entity)
2907
    {
2908 127
        $class = $this->em->getClassMetadata(get_class($entity));
2909
2910 127
        if ($class->isIdentifierComposite) {
0 ignored issues
show
Bug introduced by
Accessing isIdentifierComposite on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2911
            throw ORMInvalidArgumentException::invalidCompositeIdentifier();
2912
        }
2913
2914 127
        $values = $this->isInIdentityMap($entity)
2915 114
            ? $this->getEntityIdentifier($entity)
2916 127
            : $class->getIdentifierValues($entity);
2917
2918 127
        return isset($values[$class->identifier[0]]) ? $values[$class->identifier[0]] : null;
0 ignored issues
show
Bug introduced by
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2919
    }
2920
2921
    /**
2922
     * Tries to find an entity with the given identifier in the identity map of
2923
     * this UnitOfWork.
2924
     *
2925
     * @param mixed  $id            The entity identifier to look for.
2926
     * @param string $rootClassName The name of the root class of the mapped entity hierarchy.
2927
     *
2928
     * @return object|bool Returns the entity with the specified identifier if it exists in
2929
     *                     this UnitOfWork, FALSE otherwise.
2930
     */
2931 523
    public function tryGetById($id, $rootClassName)
2932
    {
2933 523
        $idHash = implode(' ', (array) $id);
2934
2935 523
        return isset($this->identityMap[$rootClassName][$idHash])
2936 79
            ? $this->identityMap[$rootClassName][$idHash]
2937 523
            : false;
2938
    }
2939
2940
    /**
2941
     * Schedules an entity for dirty-checking at commit-time.
2942
     *
2943
     * @param object $entity The entity to schedule for dirty-checking.
2944
     *
2945
     * @return void
2946
     *
2947
     * @todo Rename: scheduleForSynchronization
2948
     */
2949 5
    public function scheduleForDirtyCheck($entity)
2950
    {
2951 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...
2952
2953 5
        $this->scheduledForSynchronization[$rootClassName][spl_object_hash($entity)] = $entity;
2954 5
    }
2955
2956
    /**
2957
     * Checks whether the UnitOfWork has any pending insertions.
2958
     *
2959
     * @return boolean TRUE if this UnitOfWork has pending insertions, FALSE otherwise.
2960
     */
2961
    public function hasPendingInsertions()
2962
    {
2963
        return ! empty($this->entityInsertions);
2964
    }
2965
2966
    /**
2967
     * Calculates the size of the UnitOfWork. The size of the UnitOfWork is the
2968
     * number of entities in the identity map.
2969
     *
2970
     * @return integer
2971
     */
2972 1
    public function size()
2973
    {
2974 1
        $countArray = array_map('count', $this->identityMap);
2975
2976 1
        return array_sum($countArray);
2977
    }
2978
2979
    /**
2980
     * Gets the EntityPersister for an Entity.
2981
     *
2982
     * @param string $entityName The name of the Entity.
2983
     *
2984
     * @return \Doctrine\ORM\Persisters\Entity\EntityPersister
2985
     */
2986 1074
    public function getEntityPersister($entityName)
2987
    {
2988 1074
        if (isset($this->persisters[$entityName])) {
2989 849
            return $this->persisters[$entityName];
2990
        }
2991
2992 1074
        $class = $this->em->getClassMetadata($entityName);
2993
2994
        switch (true) {
2995 1074
            case ($class->isInheritanceTypeNone()):
2996 1036
                $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...
2997 1036
                break;
2998
2999 360
            case ($class->isInheritanceTypeSingleTable()):
3000 213
                $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...
3001 213
                break;
3002
3003 339
            case ($class->isInheritanceTypeJoined()):
3004 339
                $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...
3005 339
                break;
3006
3007
            default:
3008
                throw new \RuntimeException('No persister found for entity.');
3009
        }
3010
3011 1074
        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...
3012 120
            $persister = $this->em->getConfiguration()
3013 120
                ->getSecondLevelCacheConfiguration()
3014 120
                ->getCacheFactory()
3015 120
                ->buildCachedEntityPersister($this->em, $persister, $class);
3016
        }
3017
3018 1074
        $this->persisters[$entityName] = $persister;
3019
3020 1074
        return $this->persisters[$entityName];
3021
    }
3022
3023
    /**
3024
     * Gets a collection persister for a collection-valued association.
3025
     *
3026
     * @param array $association
3027
     *
3028
     * @return \Doctrine\ORM\Persisters\Collection\CollectionPersister
3029
     */
3030 570
    public function getCollectionPersister(array $association)
3031
    {
3032 570
        $role = isset($association['cache'])
3033 76
            ? $association['sourceEntity'] . '::' . $association['fieldName']
3034 570
            : $association['type'];
3035
3036 570
        if (isset($this->collectionPersisters[$role])) {
3037 449
            return $this->collectionPersisters[$role];
3038
        }
3039
3040 570
        $persister = ClassMetadata::ONE_TO_MANY === $association['type']
3041 404
            ? new OneToManyPersister($this->em)
3042 570
            : new ManyToManyPersister($this->em);
3043
3044 570
        if ($this->hasCache && isset($association['cache'])) {
3045 75
            $persister = $this->em->getConfiguration()
3046 75
                ->getSecondLevelCacheConfiguration()
3047 75
                ->getCacheFactory()
3048 75
                ->buildCachedCollectionPersister($this->em, $persister, $association);
3049
        }
3050
3051 570
        $this->collectionPersisters[$role] = $persister;
3052
3053 570
        return $this->collectionPersisters[$role];
3054
    }
3055
3056
    /**
3057
     * INTERNAL:
3058
     * Registers an entity as managed.
3059
     *
3060
     * @param object $entity The entity.
3061
     * @param array  $id     The identifier values.
3062
     * @param array  $data   The original entity data.
3063
     *
3064
     * @return void
3065
     */
3066 206
    public function registerManaged($entity, array $id, array $data)
3067
    {
3068 206
        $oid = spl_object_hash($entity);
3069
3070 206
        $this->entityIdentifiers[$oid]  = $id;
3071 206
        $this->entityStates[$oid]       = self::STATE_MANAGED;
3072 206
        $this->originalEntityData[$oid] = $data;
3073
3074 206
        $this->addToIdentityMap($entity);
3075
3076 200
        if ($entity instanceof NotifyPropertyChanged && ( ! $entity instanceof Proxy || $entity->__isInitialized())) {
3077 2
            $entity->addPropertyChangedListener($this);
3078
        }
3079 200
    }
3080
3081
    /**
3082
     * INTERNAL:
3083
     * Clears the property changeset of the entity with the given OID.
3084
     *
3085
     * @param string $oid The entity's OID.
3086
     *
3087
     * @return void
3088
     */
3089
    public function clearEntityChangeSet($oid)
3090
    {
3091
        $this->entityChangeSets[$oid] = array();
3092
    }
3093
3094
    /* PropertyChangedListener implementation */
3095
3096
    /**
3097
     * Notifies this UnitOfWork of a property change in an entity.
3098
     *
3099
     * @param object $entity       The entity that owns the property.
3100
     * @param string $propertyName The name of the property that changed.
3101
     * @param mixed  $oldValue     The old value of the property.
3102
     * @param mixed  $newValue     The new value of the property.
3103
     *
3104
     * @return void
3105
     */
3106 3
    public function propertyChanged($entity, $propertyName, $oldValue, $newValue)
3107
    {
3108 3
        $oid   = spl_object_hash($entity);
3109 3
        $class = $this->em->getClassMetadata(get_class($entity));
3110
3111 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...
3112
3113 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...
3114 1
            return; // ignore non-persistent fields
3115
        }
3116
3117
        // Update changeset and mark entity for synchronization
3118 3
        $this->entityChangeSets[$oid][$propertyName] = array($oldValue, $newValue);
3119
3120 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...
3121 3
            $this->scheduleForDirtyCheck($entity);
3122
        }
3123 3
    }
3124
3125
    /**
3126
     * Gets the currently scheduled entity insertions in this UnitOfWork.
3127
     *
3128
     * @return array
3129
     */
3130 2
    public function getScheduledEntityInsertions()
3131
    {
3132 2
        return $this->entityInsertions;
3133
    }
3134
3135
    /**
3136
     * Gets the currently scheduled entity updates in this UnitOfWork.
3137
     *
3138
     * @return array
3139
     */
3140 2
    public function getScheduledEntityUpdates()
3141
    {
3142 2
        return $this->entityUpdates;
3143
    }
3144
3145
    /**
3146
     * Gets the currently scheduled entity deletions in this UnitOfWork.
3147
     *
3148
     * @return array
3149
     */
3150 1
    public function getScheduledEntityDeletions()
3151
    {
3152 1
        return $this->entityDeletions;
3153
    }
3154
3155
    /**
3156
     * Gets the currently scheduled complete collection deletions
3157
     *
3158
     * @return array
3159
     */
3160 1
    public function getScheduledCollectionDeletions()
3161
    {
3162 1
        return $this->collectionDeletions;
3163
    }
3164
3165
    /**
3166
     * Gets the currently scheduled collection inserts, updates and deletes.
3167
     *
3168
     * @return array
3169
     */
3170
    public function getScheduledCollectionUpdates()
3171
    {
3172
        return $this->collectionUpdates;
3173
    }
3174
3175
    /**
3176
     * Helper method to initialize a lazy loading proxy or persistent collection.
3177
     *
3178
     * @param object $obj
3179
     *
3180
     * @return void
3181
     */
3182 2
    public function initializeObject($obj)
3183
    {
3184 2
        if ($obj instanceof Proxy) {
3185 1
            $obj->__load();
3186
3187 1
            return;
3188
        }
3189
3190 1
        if ($obj instanceof PersistentCollection) {
3191 1
            $obj->initialize();
3192
        }
3193 1
    }
3194
3195
    /**
3196
     * Helper method to show an object as string.
3197
     *
3198
     * @param object $obj
3199
     *
3200
     * @return string
3201
     */
3202 1
    private static function objToStr($obj)
3203
    {
3204 1
        return method_exists($obj, '__toString') ? (string) $obj : get_class($obj).'@'.spl_object_hash($obj);
3205
    }
3206
3207
    /**
3208
     * Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit().
3209
     *
3210
     * This operation cannot be undone as some parts of the UnitOfWork now keep gathering information
3211
     * on this object that might be necessary to perform a correct update.
3212
     *
3213
     * @param object $object
3214
     *
3215
     * @return void
3216
     *
3217
     * @throws ORMInvalidArgumentException
3218
     */
3219 6
    public function markReadOnly($object)
3220
    {
3221 6
        if ( ! is_object($object) || ! $this->isInIdentityMap($object)) {
3222 1
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3223
        }
3224
3225 5
        $this->readOnlyObjects[spl_object_hash($object)] = true;
3226 5
    }
3227
3228
    /**
3229
     * Is this entity read only?
3230
     *
3231
     * @param object $object
3232
     *
3233
     * @return bool
3234
     *
3235
     * @throws ORMInvalidArgumentException
3236
     */
3237 3
    public function isReadOnly($object)
3238
    {
3239 3
        if ( ! is_object($object)) {
3240
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3241
        }
3242
3243 3
        return isset($this->readOnlyObjects[spl_object_hash($object)]);
3244
    }
3245
3246
    /**
3247
     * Perform whatever processing is encapsulated here after completion of the transaction.
3248
     */
3249 1007
    private function afterTransactionComplete()
3250
    {
3251
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3252 90
            $persister->afterTransactionComplete();
3253 1007
        });
3254 1007
    }
3255
3256
    /**
3257
     * Perform whatever processing is encapsulated here after completion of the rolled-back.
3258
     */
3259
    private function afterTransactionRolledBack()
3260
    {
3261 11
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3262 3
            $persister->afterTransactionRolledBack();
3263 11
        });
3264 11
    }
3265
3266
    /**
3267
     * Performs an action after the transaction.
3268
     *
3269
     * @param callable $callback
3270
     */
3271 1012
    private function performCallbackOnCachedPersister(callable $callback)
3272
    {
3273 1012
        if ( ! $this->hasCache) {
3274 922
            return;
3275
        }
3276
3277 90
        foreach (array_merge($this->persisters, $this->collectionPersisters) as $persister) {
3278 90
            if ($persister instanceof CachedPersister) {
3279 90
                $callback($persister);
3280
            }
3281
        }
3282 90
    }
3283
3284 1016
    private function dispatchOnFlushEvent()
3285
    {
3286 1016
        if ($this->evm->hasListeners(Events::onFlush)) {
3287 4
            $this->evm->dispatchEvent(Events::onFlush, new OnFlushEventArgs($this->em));
3288
        }
3289 1016
    }
3290
3291 1011
    private function dispatchPostFlushEvent()
3292
    {
3293 1011
        if ($this->evm->hasListeners(Events::postFlush)) {
3294 5
            $this->evm->dispatchEvent(Events::postFlush, new PostFlushEventArgs($this->em));
3295
        }
3296 1010
    }
3297
3298
    /**
3299
     * Verifies if two given entities actually are the same based on identifier comparison
3300
     *
3301
     * @param object $entity1
3302
     * @param object $entity2
3303
     *
3304
     * @return bool
3305
     */
3306 14
    private function isIdentifierEquals($entity1, $entity2)
3307
    {
3308 14
        if ($entity1 === $entity2) {
3309
            return true;
3310
        }
3311
3312 14
        $class = $this->em->getClassMetadata(get_class($entity1));
3313
3314 14
        if ($class !== $this->em->getClassMetadata(get_class($entity2))) {
3315 11
            return false;
3316
        }
3317
3318 3
        $oid1 = spl_object_hash($entity1);
3319 3
        $oid2 = spl_object_hash($entity2);
3320
3321 3
        $id1 = isset($this->entityIdentifiers[$oid1])
3322 3
            ? $this->entityIdentifiers[$oid1]
3323 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...
3324 3
        $id2 = isset($this->entityIdentifiers[$oid2])
3325 3
            ? $this->entityIdentifiers[$oid2]
3326 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...
3327
3328 3
        return $id1 === $id2 || implode(' ', $id1) === implode(' ', $id2);
3329
    }
3330
3331
    /**
3332
     * @param object $entity
3333
     * @param object $managedCopy
3334
     *
3335
     * @throws ORMException
3336
     * @throws OptimisticLockException
3337
     * @throws TransactionRequiredException
3338
     */
3339 31
    private function mergeEntityStateIntoManagedCopy($entity, $managedCopy)
3340
    {
3341 31
        $class = $this->em->getClassMetadata(get_class($entity));
3342
3343 31
        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...
3344 31
            $name = $prop->name;
3345
3346 31
            $prop->setAccessible(true);
3347
3348 31
            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...
3349 31
                if ( ! $class->isIdentifier($name)) {
3350 31
                    $prop->setValue($managedCopy, $prop->getValue($entity));
3351
                }
3352
            } else {
3353 29
                $assoc2 = $class->associationMappings[$name];
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
3354
3355 29
                if ($assoc2['type'] & ClassMetadata::TO_ONE) {
3356 25
                    $other = $prop->getValue($entity);
3357 25
                    if ($other === null) {
3358 12
                        $prop->setValue($managedCopy, null);
3359
                    } else {
3360 16
                        if ($other instanceof Proxy && !$other->__isInitialized()) {
3361
                            // do not merge fields marked lazy that have not been fetched.
3362 4
                            continue;
3363
                        }
3364
3365 12
                        if ( ! $assoc2['isCascadeMerge']) {
3366 6
                            if ($this->getEntityState($other) === self::STATE_DETACHED) {
3367 3
                                $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']);
3368 3
                                $relatedId   = $targetClass->getIdentifierValues($other);
3369
3370 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...
3371 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...
3372
                                } else {
3373 1
                                    $other = $this->em->getProxyFactory()->getProxy(
3374 1
                                        $assoc2['targetEntity'],
3375
                                        $relatedId
3376
                                    );
3377 1
                                    $this->registerManaged($other, $relatedId, array());
3378
                                }
3379
                            }
3380
3381 21
                            $prop->setValue($managedCopy, $other);
3382
                        }
3383
                    }
3384
                } else {
3385 17
                    $mergeCol = $prop->getValue($entity);
3386
3387 17
                    if ($mergeCol instanceof PersistentCollection && ! $mergeCol->isInitialized()) {
3388
                        // do not merge fields marked lazy that have not been fetched.
3389
                        // keep the lazy persistent collection of the managed copy.
3390 5
                        continue;
3391
                    }
3392
3393 14
                    $managedCol = $prop->getValue($managedCopy);
3394
3395 14
                    if ( ! $managedCol) {
3396 4
                        $managedCol = new PersistentCollection(
3397 4
                            $this->em,
3398 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...
3399 4
                            new ArrayCollection
3400
                        );
3401 4
                        $managedCol->setOwner($managedCopy, $assoc2);
3402 4
                        $prop->setValue($managedCopy, $managedCol);
3403
                    }
3404
3405 14
                    if ($assoc2['isCascadeMerge']) {
3406 9
                        $managedCol->initialize();
3407
3408
                        // clear and set dirty a managed collection if its not also the same collection to merge from.
3409 9
                        if ( ! $managedCol->isEmpty() && $managedCol !== $mergeCol) {
3410 1
                            $managedCol->unwrap()->clear();
3411 1
                            $managedCol->setDirty(true);
3412
3413 1
                            if ($assoc2['isOwningSide']
3414 1
                                && $assoc2['type'] == ClassMetadata::MANY_TO_MANY
3415 1
                                && $class->isChangeTrackingNotify()
3416
                            ) {
3417
                                $this->scheduleForDirtyCheck($managedCopy);
3418
                            }
3419
                        }
3420
                    }
3421
                }
3422
            }
3423
3424 31
            if ($class->isChangeTrackingNotify()) {
3425
                // Just treat all properties as changed, there is no other choice.
3426 31
                $this->propertyChanged($managedCopy, $name, null, $prop->getValue($managedCopy));
3427
            }
3428
        }
3429 31
    }
3430
3431
    /**
3432
     * This method called by hydrators, and indicates that hydrator totally completed current hydration cycle.
3433
     * Unit of work able to fire deferred events, related to loading events here.
3434
     *
3435
     * @internal should be called internally from object hydrators
3436
     */
3437 880
    public function hydrationComplete()
3438
    {
3439 880
        $this->hydrationCompleteHandler->hydrationComplete();
3440 880
    }
3441
3442
    /**
3443
     * @param string $entityName
3444
     */
3445 3
    private function clearIdentityMapForEntityName($entityName)
3446
    {
3447 3
        if (! isset($this->identityMap[$entityName])) {
3448
            return;
3449
        }
3450
3451 3
        $visited = [];
3452
3453 3
        foreach ($this->identityMap[$entityName] as $entity) {
3454 3
            $this->doDetach($entity, $visited, false);
3455
        }
3456 3
    }
3457
3458
    /**
3459
     * @param string $entityName
3460
     */
3461 3
    private function clearEntityInsertionsForEntityName($entityName)
3462
    {
3463 3
        foreach ($this->entityInsertions as $hash => $entity) {
3464
            // note: performance optimization - `instanceof` is much faster than a function call
3465 1
            if ($entity instanceof $entityName && get_class($entity) === $entityName) {
3466 1
                unset($this->entityInsertions[$hash]);
3467
            }
3468
        }
3469 3
    }
3470
}
3471