Completed
Pull Request — master (#5924)
by Marco
27:45 queued 19:39
created

UnitOfWork::hydrationComplete()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM;
21
22
use Doctrine\Common\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 2338
    public function __construct(EntityManagerInterface $em)
293
    {
294 2338
        $this->em                         = $em;
295 2338
        $this->evm                        = $em->getEventManager();
296 2338
        $this->listenersInvoker           = new ListenersInvoker($em);
297 2338
        $this->hasCache                   = $em->getConfiguration()->isSecondLevelCacheEnabled();
298 2338
        $this->identifierFlattener        = new IdentifierFlattener($this, $em->getMetadataFactory());
299 2338
        $this->hydrationCompleteHandler   = new HydrationCompleteHandler($this->listenersInvoker, $em);
300 2338
        $this->reflectionPropertiesGetter = new ReflectionPropertiesGetter(new RuntimeReflectionService());
301 2338
    }
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 1014
    public function commit($entity = null)
323
    {
324
        // Raise preFlush
325 1014
        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 1014
        if ($entity === null) {
331 1006
            $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 1011
        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 1011
                $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 1007
        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 1007
        $this->dispatchOnFlushEvent();
359
360
        // Now we need a commit order to maintain referential integrity
361 1007
        $commitOrder = $this->getCommitOrder();
362
363 1007
        $conn = $this->em->getConnection();
364 1007
        $conn->beginTransaction();
365
366
        try {
367
            // Collection deletions (deletions of complete collections)
368 1007
            foreach ($this->collectionDeletions as $collectionToDelete) {
369 19
                $this->getCollectionPersister($collectionToDelete->getMapping())->delete($collectionToDelete);
370
            }
371
372 1007
            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 1003
                foreach ($commitOrder as $class) {
374 1003
                    $this->executeInserts($class);
375
                }
376
            }
377
378 1006
            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 1002
            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 1002
            foreach ($this->collectionUpdates as $collectionToUpdate) {
391 530
                $this->getCollectionPersister($collectionToUpdate->getMapping())->update($collectionToUpdate);
392
            }
393
394
            // Entity deletions come last and need to be in reverse commit order
395 1002
            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 1002
            $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 1002
        $this->afterTransactionComplete();
412
413
        // Take new snapshots from visited collections
414 1002
        foreach ($this->visitedCollections as $coll) {
415 529
            $coll->takeSnapshot();
416
        }
417
418 1002
        $this->dispatchPostFlushEvent();
419
420
        // Clear up
421 1001
        $this->entityInsertions =
422 1001
        $this->entityUpdates =
423 1001
        $this->entityDeletions =
424 1001
        $this->extraUpdates =
425 1001
        $this->entityChangeSets =
426 1001
        $this->collectionUpdates =
427 1001
        $this->collectionDeletions =
428 1001
        $this->visitedCollections =
429 1001
        $this->scheduledForSynchronization =
430 1001
        $this->orphanRemovals = array();
431 1001
    }
432
433
    /**
434
     * Computes the changesets of all entities scheduled for insertion.
435
     *
436
     * @return void
437
     */
438 1013
    private function computeScheduleInsertsChangeSets()
439
    {
440 1013
        foreach ($this->entityInsertions as $entity) {
441 1005
            $class = $this->em->getClassMetadata(get_class($entity));
442
443 1005
            $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 1011
    }
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 1005
    public function & getEntityChangeSet($entity)
518
    {
519 1005
        $oid  = spl_object_hash($entity);
520 1005
        $data = array();
521
522 1005
        if (!isset($this->entityChangeSets[$oid])) {
523 1
            return $data;
524
        }
525
526 1005
        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 1015
    public function computeChangeSet(ClassMetadata $class, $entity)
564
    {
565 1015
        $oid = spl_object_hash($entity);
566
567 1015
        if (isset($this->readOnlyObjects[$oid])) {
568 2
            return;
569
        }
570
571 1015
        if ( ! $class->isInheritanceTypeNone()) {
572 307
            $class = $this->em->getClassMetadata(get_class($entity));
573
        }
574
575 1015
        $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 1015
        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 1015
        $actualData = array();
582
583 1015
        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 1015
            $value = $refProp->getValue($entity);
585
586 1015
            if ($class->isCollectionValuedAssociation($name) && $value !== null) {
587 776
                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 771
                if ( ! $value instanceof Collection) {
597 242
                    $value = new ArrayCollection($value);
598
                }
599
600 771
                $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 771
                $value = new PersistentCollection(
604 771
                    $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 771
                $value->setOwner($entity, $assoc);
607 771
                $value->setDirty( ! $value->isEmpty());
608
609 771
                $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 771
                $actualData[$name] = $value;
612
613 771
                continue;
614
            }
615
616 1015
            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 1015
                $actualData[$name] = $value;
618
            }
619
        }
620
621 1015
        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 1011
            $this->originalEntityData[$oid] = $actualData;
625 1011
            $changeSet = array();
626
627 1011
            foreach ($actualData as $propName => $actualValue) {
628 996
                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 945
                    $changeSet[$propName] = array(null, $actualValue);
630
631 945
                    continue;
632
                }
633
634 893
                $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 893
                if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
637 893
                    $changeSet[$propName] = array(null, $actualValue);
638
                }
639
            }
640
641 1011
            $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 263
            $originalData           = $this->originalEntityData[$oid];
646 263
            $isChangeTrackingNotify = $class->isChangeTrackingNotify();
647 263
            $changeSet              = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid]))
648
                ? $this->entityChangeSets[$oid]
649 263
                : array();
650
651 263
            foreach ($actualData as $propName => $actualValue) {
652
                // skip field, its a partially omitted one!
653 248
                if ( ! (isset($originalData[$propName]) || array_key_exists($propName, $originalData))) {
654 8
                    continue;
655
                }
656
657 248
                $orgValue = $originalData[$propName];
658
659
                // skip if value haven't changed
660 248
                if ($orgValue === $actualValue) {
661 232
                    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 263
            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 1015
        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 893
            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 638
                continue;
730
            }
731
732 864
            $this->computeAssociationChanges($assoc, $val);
733
734 856
            if ( ! isset($this->entityChangeSets[$oid]) &&
735 856
                $assoc['isOwningSide'] &&
736 856
                $assoc['type'] == ClassMetadata::MANY_TO_MANY &&
737 856
                $val instanceof PersistentCollection &&
738 856
                $val->isDirty()) {
739
740 35
                $this->entityChangeSets[$oid]   = array();
741 35
                $this->originalEntityData[$oid] = $actualData;
742 856
                $this->entityUpdates[$oid]      = $entity;
743
            }
744
        }
745 1007
    }
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 1006
    public function computeChangeSets()
755
    {
756
        // Compute changes for INSERTed entities first. This must always happen.
757 1006
        $this->computeScheduleInsertsChangeSets();
758
759
        // Compute changes for other MANAGED entities. Change tracking policies take effect here.
760 1004
        foreach ($this->identityMap as $className => $entities) {
761 447
            $class = $this->em->getClassMetadata($className);
762
763
            // Skip class if instances are read-only
764 447
            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 446
                case ($class->isChangeTrackingDeferredImplicit()):
772 444
                    $entitiesToProcess = $entities;
773 444
                    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 446
            foreach ($entitiesToProcess as $entity) {
785
                // Ignore uninitialized proxy objects
786 426
                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 425
                $oid = spl_object_hash($entity);
792
793 425
                if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) {
794 446
                    $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 1004
    }
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 864
    private function computeAssociationChanges($assoc, $value)
812
    {
813 864
        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 863
        if ($value instanceof PersistentCollection && $value->isDirty()) {
818 532
            $coid = spl_object_hash($value);
819
820 532
            $this->collectionUpdates[$coid] = $value;
821 532
            $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 863
        $unwrappedValue = ($assoc['type'] & ClassMetadata::TO_ONE) ? array($value) : $value->unwrap();
828 863
        $targetClass    = $this->em->getClassMetadata($assoc['targetEntity']);
829
830 863
        foreach ($unwrappedValue as $key => $entry) {
831 721
            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 715
            $state = $this->getEntityState($entry, self::STATE_NEW);
836
837 715
            if ( ! ($entry instanceof $assoc['targetEntity'])) {
838
                throw ORMException::unexpectedAssociationValue($assoc['sourceEntity'], $assoc['fieldName'], get_class($entry), $assoc['targetEntity']);
839
            }
840
841
            switch ($state) {
842 715
                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 709
                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 709
                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 712
                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 855
    }
871
872
    /**
873
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
874
     * @param object                              $entity
875
     *
876
     * @return void
877
     */
878 1030
    private function persistNew($class, $entity)
879
    {
880 1030
        $oid    = spl_object_hash($entity);
881 1030
        $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::prePersist);
882
883 1030
        if ($invoke !== ListenersInvoker::INVOKE_NONE) {
884 139
            $this->listenersInvoker->invoke($class, Events::prePersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
885
        }
886
887 1030
        $idGen = $class->idGenerator;
888
889 1030
        if ( ! $idGen->isPostInsertGenerator()) {
890 268
            $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 268
            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 268
            $this->entityIdentifiers[$oid] = $idValue;
899
        }
900
901 1030
        $this->entityStates[$oid] = self::STATE_MANAGED;
902
903 1030
        $this->scheduleForInsert($entity);
904 1030
    }
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 1003
    private function executeInserts($class)
985
    {
986 1003
        $entities   = array();
987 1003
        $className  = $class->name;
988 1003
        $persister  = $this->getEntityPersister($className);
989 1003
        $invoke     = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist);
990
991 1003
        foreach ($this->entityInsertions as $oid => $entity) {
992
993 1003
            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 854
                continue;
995
            }
996
997 1003
            $persister->addInsert($entity);
998
999 1003
            unset($this->entityInsertions[$oid]);
1000
1001 1003
            if ($invoke !== ListenersInvoker::INVOKE_NONE) {
1002 1003
                $entities[] = $entity;
1003
            }
1004
        }
1005
1006 1003
        $postInsertIds = $persister->executeInserts();
1007
1008 1003
        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 917
            foreach ($postInsertIds as $postInsertId) {
1011 917
                $id      = $postInsertId['generatedId'];
1012 917
                $entity  = $postInsertId['entity'];
1013 917
                $oid     = spl_object_hash($entity);
1014 917
                $idField = $class->identifier[0];
1015
1016 917
                $class->reflFields[$idField]->setValue($entity, $id);
1017
1018 917
                $this->entityIdentifiers[$oid] = array($idField => $id);
1019 917
                $this->entityStates[$oid] = self::STATE_MANAGED;
1020 917
                $this->originalEntityData[$oid][$idField] = $id;
1021
1022 917
                $this->addToIdentityMap($entity);
1023
            }
1024
        }
1025
1026 1003
        foreach ($entities as $entity) {
1027 135
            $this->listenersInvoker->invoke($class, Events::postPersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
1028
        }
1029 1003
    }
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 1007
    private function getCommitOrder(array $entityChangeSet = null)
1116
    {
1117 1007
        if ($entityChangeSet === null) {
1118 1007
            $entityChangeSet = array_merge($this->entityInsertions, $this->entityUpdates, $this->entityDeletions);
1119
        }
1120
1121 1007
        $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 1007
        $newNodes = array();
1129
1130 1007
        foreach ($entityChangeSet as $entity) {
1131 1007
            $class = $this->em->getClassMetadata(get_class($entity));
1132
1133 1007
            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 624
                continue;
1135
            }
1136
1137 1007
            $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 1007
            $newNodes[] = $class;
1140
        }
1141
1142
        // Calculate dependencies for new nodes
1143 1007
        while ($class = array_pop($newNodes)) {
1144 1007
            foreach ($class->associationMappings as $assoc) {
1145 885
                if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) {
1146 845
                    continue;
1147
                }
1148
1149 838
                $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
1150
1151 838
                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 649
                    $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 649
                    $newNodes[] = $targetClass;
1155
                }
1156
1157 838
                $joinColumns = reset($assoc['joinColumns']);
1158
1159 838
                $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 838
                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 831
                    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 1007
        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 1031
    public function scheduleForInsert($entity)
1195
    {
1196 1031
        $oid = spl_object_hash($entity);
1197
1198 1031
        if (isset($this->entityUpdates[$oid])) {
1199
            throw new InvalidArgumentException("Dirty entity can not be scheduled for insertion.");
1200
        }
1201
1202 1031
        if (isset($this->entityDeletions[$oid])) {
1203 1
            throw ORMInvalidArgumentException::scheduleInsertForRemovedEntity($entity);
1204
        }
1205 1031
        if (isset($this->originalEntityData[$oid]) && ! isset($this->entityInsertions[$oid])) {
1206 1
            throw ORMInvalidArgumentException::scheduleInsertForManagedEntity($entity);
1207
        }
1208
1209 1031
        if (isset($this->entityInsertions[$oid])) {
1210 1
            throw ORMInvalidArgumentException::scheduleInsertTwice($entity);
1211
        }
1212
1213 1031
        $this->entityInsertions[$oid] = $entity;
1214
1215 1031
        if (isset($this->entityIdentifiers[$oid])) {
1216 268
            $this->addToIdentityMap($entity);
1217
        }
1218
1219 1031
        if ($entity instanceof NotifyPropertyChanged) {
1220 5
            $entity->addPropertyChangedListener($this);
1221
        }
1222 1031
    }
1223
1224
    /**
1225
     * Checks whether an entity is scheduled for insertion.
1226
     *
1227
     * @param object $entity
1228
     *
1229
     * @return boolean
1230
     */
1231 630
    public function isScheduledForInsert($entity)
1232
    {
1233 630
        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 1097
    public function addToIdentityMap($entity)
1399
    {
1400 1097
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1401 1097
        $identifier    = $this->entityIdentifiers[spl_object_hash($entity)];
1402
1403 1097
        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 1091
        $idHash    = implode(' ', $identifier);
1408 1091
        $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 1091
        if (isset($this->identityMap[$className][$idHash])) {
1411 83
            return false;
1412
        }
1413
1414 1091
        $this->identityMap[$className][$idHash] = $entity;
1415
1416 1091
        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 1044
    public function getEntityState($entity, $assume = null)
1431
    {
1432 1044
        $oid = spl_object_hash($entity);
1433
1434 1044
        if (isset($this->entityStates[$oid])) {
1435 783
            return $this->entityStates[$oid];
1436
        }
1437
1438 1038
        if ($assume !== null) {
1439 1034
            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 213
    public function isInIdentityMap($entity)
1582
    {
1583 213
        $oid = spl_object_hash($entity);
1584
1585 213
        if ( ! isset($this->entityIdentifiers[$oid])) {
1586 31
            return false;
1587
        }
1588
1589 198
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1590 198
        $idHash        = implode(' ', $this->entityIdentifiers[$oid]);
1591
1592 198
        if ($idHash === '') {
1593
            return false;
1594
        }
1595
1596 198
        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...
1597
    }
1598
1599
    /**
1600
     * INTERNAL:
1601
     * Checks whether an identifier hash exists in the identity map.
1602
     *
1603
     * @ignore
1604
     *
1605
     * @param string $idHash
1606
     * @param string $rootClassName
1607
     *
1608
     * @return boolean
1609
     */
1610
    public function containsIdHash($idHash, $rootClassName)
1611
    {
1612
        return isset($this->identityMap[$rootClassName][$idHash]);
1613
    }
1614
1615
    /**
1616
     * Persists an entity as part of the current unit of work.
1617
     *
1618
     * @param object $entity The entity to persist.
1619
     *
1620
     * @return void
1621
     */
1622 1027
    public function persist($entity)
1623
    {
1624 1027
        $visited = array();
1625
1626 1027
        $this->doPersist($entity, $visited);
1627 1020
    }
1628
1629
    /**
1630
     * Persists an entity as part of the current unit of work.
1631
     *
1632
     * This method is internally called during persist() cascades as it tracks
1633
     * the already visited entities to prevent infinite recursions.
1634
     *
1635
     * @param object $entity  The entity to persist.
1636
     * @param array  $visited The already visited entities.
1637
     *
1638
     * @return void
1639
     *
1640
     * @throws ORMInvalidArgumentException
1641
     * @throws UnexpectedValueException
1642
     */
1643 1027
    private function doPersist($entity, array &$visited)
1644
    {
1645 1027
        $oid = spl_object_hash($entity);
1646
1647 1027
        if (isset($visited[$oid])) {
1648 109
            return; // Prevent infinite recursion
1649
        }
1650
1651 1027
        $visited[$oid] = $entity; // Mark visited
1652
1653 1027
        $class = $this->em->getClassMetadata(get_class($entity));
1654
1655
        // We assume NEW, so DETACHED entities result in an exception on flush (constraint violation).
1656
        // If we would detect DETACHED here we would throw an exception anyway with the same
1657
        // consequences (not recoverable/programming error), so just assuming NEW here
1658
        // lets us avoid some database lookups for entities with natural identifiers.
1659 1027
        $entityState = $this->getEntityState($entity, self::STATE_NEW);
1660
1661
        switch ($entityState) {
1662 1027
            case self::STATE_MANAGED:
1663
                // Nothing to do, except if policy is "deferred explicit"
1664 234
                if ($class->isChangeTrackingDeferredExplicit()) {
1665 2
                    $this->scheduleForDirtyCheck($entity);
1666
                }
1667 234
                break;
1668
1669 1027
            case self::STATE_NEW:
1670 1026
                $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...
1671 1026
                break;
1672
1673 1
            case self::STATE_REMOVED:
1674
                // Entity becomes managed again
1675 1
                unset($this->entityDeletions[$oid]);
1676 1
                $this->addToIdentityMap($entity);
1677
1678 1
                $this->entityStates[$oid] = self::STATE_MANAGED;
1679 1
                break;
1680
1681
            case self::STATE_DETACHED:
1682
                // Can actually not happen right now since we assume STATE_NEW.
1683
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "persisted");
1684
1685
            default:
1686
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1687
        }
1688
1689 1027
        $this->cascadePersist($entity, $visited);
1690 1020
    }
1691
1692
    /**
1693
     * Deletes an entity as part of the current unit of work.
1694
     *
1695
     * @param object $entity The entity to remove.
1696
     *
1697
     * @return void
1698
     */
1699 65
    public function remove($entity)
1700
    {
1701 65
        $visited = array();
1702
1703 65
        $this->doRemove($entity, $visited);
1704 65
    }
1705
1706
    /**
1707
     * Deletes an entity as part of the current unit of work.
1708
     *
1709
     * This method is internally called during delete() cascades as it tracks
1710
     * the already visited entities to prevent infinite recursions.
1711
     *
1712
     * @param object $entity  The entity to delete.
1713
     * @param array  $visited The map of the already visited entities.
1714
     *
1715
     * @return void
1716
     *
1717
     * @throws ORMInvalidArgumentException If the instance is a detached entity.
1718
     * @throws UnexpectedValueException
1719
     */
1720 65
    private function doRemove($entity, array &$visited)
1721
    {
1722 65
        $oid = spl_object_hash($entity);
1723
1724 65
        if (isset($visited[$oid])) {
1725 1
            return; // Prevent infinite recursion
1726
        }
1727
1728 65
        $visited[$oid] = $entity; // mark visited
1729
1730
        // Cascade first, because scheduleForDelete() removes the entity from the identity map, which
1731
        // can cause problems when a lazy proxy has to be initialized for the cascade operation.
1732 65
        $this->cascadeRemove($entity, $visited);
1733
1734 65
        $class       = $this->em->getClassMetadata(get_class($entity));
1735 65
        $entityState = $this->getEntityState($entity);
1736
1737
        switch ($entityState) {
1738 65
            case self::STATE_NEW:
1739 65
            case self::STATE_REMOVED:
1740
                // nothing to do
1741 2
                break;
1742
1743 65
            case self::STATE_MANAGED:
1744 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...
1745
1746 65
                if ($invoke !== ListenersInvoker::INVOKE_NONE) {
1747 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...
1748
                }
1749
1750 65
                $this->scheduleForDelete($entity);
1751 65
                break;
1752
1753
            case self::STATE_DETACHED:
1754
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "removed");
1755
            default:
1756
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1757
        }
1758
1759 65
    }
1760
1761
    /**
1762
     * Merges the state of the given detached entity into this UnitOfWork.
1763
     *
1764
     * @param object $entity
1765
     *
1766
     * @return object The managed copy of the entity.
1767
     *
1768
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1769
     *         attribute and the version check against the managed copy fails.
1770
     *
1771
     * @todo Require active transaction!? OptimisticLockException may result in undefined state!?
1772
     */
1773 40
    public function merge($entity)
1774
    {
1775 40
        $visited = array();
1776
1777 40
        return $this->doMerge($entity, $visited);
1778
    }
1779
1780
    /**
1781
     * Executes a merge operation on an entity.
1782
     *
1783
     * @param object      $entity
1784
     * @param array       $visited
1785
     * @param object|null $prevManagedCopy
1786
     * @param array|null  $assoc
1787
     *
1788
     * @return object The managed copy of the entity.
1789
     *
1790
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1791
     *         attribute and the version check against the managed copy fails.
1792
     * @throws ORMInvalidArgumentException If the entity instance is NEW.
1793
     * @throws EntityNotFoundException
1794
     */
1795 40
    private function doMerge($entity, array &$visited, $prevManagedCopy = null, array $assoc = [])
1796
    {
1797 40
        $oid = spl_object_hash($entity);
1798
1799 40
        if (isset($visited[$oid])) {
1800 4
            $managedCopy = $visited[$oid];
1801
1802 4
            if ($prevManagedCopy !== null) {
1803 4
                $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy);
1804
            }
1805
1806 4
            return $managedCopy;
1807
        }
1808
1809 40
        $class = $this->em->getClassMetadata(get_class($entity));
1810
1811
        // First we assume DETACHED, although it can still be NEW but we can avoid
1812
        // an extra db-roundtrip this way. If it is not MANAGED but has an identity,
1813
        // we need to fetch it from the db anyway in order to merge.
1814
        // MANAGED entities are ignored by the merge operation.
1815 40
        $managedCopy = $entity;
1816
1817 40
        if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) {
1818
            // Try to look the entity up in the identity map.
1819 39
            $id = $class->getIdentifierValues($entity);
1820
1821
            // If there is no ID, it is actually NEW.
1822 39
            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...
1823 5
                $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...
1824
1825 5
                $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...
1826
            } else {
1827 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...
1828 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...
1829 35
                    : $id;
1830
1831 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 1895 which is incompatible with the return type documented by Doctrine\ORM\UnitOfWork::doMerge of type object.
Loading history...
1832
1833 35
                if ($managedCopy) {
1834
                    // We have the entity in-memory already, just make sure its not removed.
1835 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 1831 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...
1836 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 1831 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...
1837
                    }
1838
                } else {
1839
                    // We need to fetch the managed copy in order to merge.
1840 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...
1841
                }
1842
1843 35
                if ($managedCopy === null) {
1844
                    // If the identifier is ASSIGNED, it is NEW, otherwise an error
1845
                    // since the managed entity was not found.
1846 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...
1847 1
                        throw EntityNotFoundException::fromClassNameAndIdentifier(
1848 1
                            $class->getName(),
1849 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...
1850
                        );
1851
                    }
1852
1853 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...
1854 1
                    $class->setIdentifierValues($managedCopy, $id);
1855
1856 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...
1857
                }
1858
            }
1859
1860 38
            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 1831 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...
1861 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...
1862 4
                $managedCopyVersion = $reflField->getValue($managedCopy);
1863 4
                $entityVersion      = $reflField->getValue($entity);
1864
1865
                // Throw exception if versions don't match.
1866 4
                if ($managedCopyVersion != $entityVersion) {
1867 1
                    throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion);
1868
                }
1869
            }
1870
1871 37
            $visited[$oid] = $managedCopy; // mark visited
1872
1873 37
            if ($this->isLoaded($entity)) {
1874 30
                if ($managedCopy instanceof Proxy && ! $managedCopy->__isInitialized()) {
1875 4
                    $managedCopy->__load();
1876
                }
1877
1878 30
                $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy);
0 ignored issues
show
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1831 can also be of type boolean; however, Doctrine\ORM\UnitOfWork:...yStateIntoManagedCopy() does only seem to accept object, maybe add an additional type check?

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

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

    return array();
}

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

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

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

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2160 1
                    continue;
2161
                }
2162
2163 8
                if ($relatedEntities instanceof PersistentCollection) {
2164
                    // Unwrap so that foreach() does not initialize
2165 5
                    $relatedEntities = $relatedEntities->unwrap();
2166
                }
2167
2168 8
                foreach ($relatedEntities as $relatedEntity) {
2169 8
                    $this->doMerge($relatedEntity, $visited, $managedCopy, $assoc);
2170
                }
2171 7
            } else if ($relatedEntities !== null) {
2172 14
                $this->doMerge($relatedEntities, $visited, $managedCopy, $assoc);
2173
            }
2174
        }
2175 38
    }
2176
2177
    /**
2178
     * Cascades the save operation to associated entities.
2179
     *
2180
     * @param object $entity
2181
     * @param array  $visited
2182
     *
2183
     * @return void
2184
     */
2185 1027
    private function cascadePersist($entity, array &$visited)
2186
    {
2187 1027
        $class = $this->em->getClassMetadata(get_class($entity));
2188
2189 1027
        $associationMappings = array_filter(
2190 1027
            $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...
2191
            function ($assoc) { return $assoc['isCascadePersist']; }
2192
        );
2193
2194 1027
        foreach ($associationMappings as $assoc) {
2195 649
            $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...
2196
2197
            switch (true) {
2198 649
                case ($relatedEntities instanceof PersistentCollection):
2199
                    // Unwrap so that foreach() does not initialize
2200 21
                    $relatedEntities = $relatedEntities->unwrap();
2201
                    // break; is commented intentionally!
2202
2203
                case ($relatedEntities instanceof Collection):
2204 589
                case (is_array($relatedEntities)):
2205 553
                    if (($assoc['type'] & ClassMetadata::TO_MANY) <= 0) {
2206 3
                        throw ORMInvalidArgumentException::invalidAssociation(
2207 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...
2208
                            $assoc,
2209
                            $relatedEntities
2210
                        );
2211
                    }
2212
2213 550
                    foreach ($relatedEntities as $relatedEntity) {
2214 282
                        $this->doPersist($relatedEntity, $visited);
2215
                    }
2216
2217 550
                    break;
2218
2219 579
                case ($relatedEntities !== null):
2220 246
                    if (! $relatedEntities instanceof $assoc['targetEntity']) {
2221 4
                        throw ORMInvalidArgumentException::invalidAssociation(
2222 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...
2223
                            $assoc,
2224
                            $relatedEntities
2225
                        );
2226
                    }
2227
2228 242
                    $this->doPersist($relatedEntities, $visited);
2229 242
                    break;
2230
2231 643
                default:
2232
                    // Do nothing
2233
            }
2234
        }
2235 1020
    }
2236
2237
    /**
2238
     * Cascades the delete operation to associated entities.
2239
     *
2240
     * @param object $entity
2241
     * @param array  $visited
2242
     *
2243
     * @return void
2244
     */
2245 65
    private function cascadeRemove($entity, array &$visited)
2246
    {
2247 65
        $class = $this->em->getClassMetadata(get_class($entity));
2248
2249 65
        $associationMappings = array_filter(
2250 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...
2251
            function ($assoc) { return $assoc['isCascadeRemove']; }
2252
        );
2253
2254 65
        $entitiesToCascade = array();
2255
2256 65
        foreach ($associationMappings as $assoc) {
2257 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...
2258 6
                $entity->__load();
2259
            }
2260
2261 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...
2262
2263
            switch (true) {
2264 26
                case ($relatedEntities instanceof Collection):
2265 19
                case (is_array($relatedEntities)):
2266
                    // If its a PersistentCollection initialization is intended! No unwrap!
2267 20
                    foreach ($relatedEntities as $relatedEntity) {
2268 10
                        $entitiesToCascade[] = $relatedEntity;
2269
                    }
2270 20
                    break;
2271
2272 19
                case ($relatedEntities !== null):
2273 7
                    $entitiesToCascade[] = $relatedEntities;
2274 7
                    break;
2275
2276 26
                default:
2277
                    // Do nothing
2278
            }
2279
        }
2280
2281 65
        foreach ($entitiesToCascade as $relatedEntity) {
2282 16
            $this->doRemove($relatedEntity, $visited);
2283
        }
2284 65
    }
2285
2286
    /**
2287
     * Acquire a lock on the given entity.
2288
     *
2289
     * @param object $entity
2290
     * @param int    $lockMode
2291
     * @param int    $lockVersion
2292
     *
2293
     * @return void
2294
     *
2295
     * @throws ORMInvalidArgumentException
2296
     * @throws TransactionRequiredException
2297
     * @throws OptimisticLockException
2298
     */
2299 11
    public function lock($entity, $lockMode, $lockVersion = null)
2300
    {
2301 11
        if ($entity === null) {
2302 1
            throw new \InvalidArgumentException("No entity passed to UnitOfWork#lock().");
2303
        }
2304
2305 10
        if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) {
2306 1
            throw ORMInvalidArgumentException::entityNotManaged($entity);
2307
        }
2308
2309 9
        $class = $this->em->getClassMetadata(get_class($entity));
2310
2311
        switch (true) {
2312 9
            case LockMode::OPTIMISTIC === $lockMode:
2313 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...
2314 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...
2315
                }
2316
2317 4
                if ($lockVersion === null) {
2318
                    return;
2319
                }
2320
2321 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...
2322 1
                    $entity->__load();
2323
                }
2324
2325 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...
2326
2327 4
                if ($entityVersion != $lockVersion) {
2328 2
                    throw OptimisticLockException::lockFailedVersionMismatch($entity, $lockVersion, $entityVersion);
2329
                }
2330
2331 2
                break;
2332
2333 3
            case LockMode::NONE === $lockMode:
2334 3
            case LockMode::PESSIMISTIC_READ === $lockMode:
2335 1
            case LockMode::PESSIMISTIC_WRITE === $lockMode:
2336 3
                if (!$this->em->getConnection()->isTransactionActive()) {
2337 2
                    throw TransactionRequiredException::transactionRequired();
2338
                }
2339
2340 1
                $oid = spl_object_hash($entity);
2341
2342 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...
2343 1
                    array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
2344
                    $lockMode
2345
                );
2346 1
                break;
2347
2348
            default:
2349
                // Do nothing
2350
        }
2351 3
    }
2352
2353
    /**
2354
     * Gets the CommitOrderCalculator used by the UnitOfWork to order commits.
2355
     *
2356
     * @return \Doctrine\ORM\Internal\CommitOrderCalculator
2357
     */
2358 1007
    public function getCommitOrderCalculator()
2359
    {
2360 1007
        return new Internal\CommitOrderCalculator();
2361
    }
2362
2363
    /**
2364
     * Clears the UnitOfWork.
2365
     *
2366
     * @param string|null $entityName if given, only entities of this type will get detached.
2367
     *
2368
     * @return void
2369
     */
2370 1218
    public function clear($entityName = null)
2371
    {
2372 1218
        if ($entityName === null) {
2373 1217
            $this->identityMap =
2374 1217
            $this->entityIdentifiers =
2375 1217
            $this->originalEntityData =
2376 1217
            $this->entityChangeSets =
2377 1217
            $this->entityStates =
2378 1217
            $this->scheduledForSynchronization =
2379 1217
            $this->entityInsertions =
2380 1217
            $this->entityUpdates =
2381 1217
            $this->entityDeletions =
2382 1217
            $this->collectionDeletions =
2383 1217
            $this->collectionUpdates =
2384 1217
            $this->extraUpdates =
2385 1217
            $this->readOnlyObjects =
2386 1217
            $this->visitedCollections =
2387 1217
            $this->orphanRemovals = array();
2388
        } else {
2389 3
            $this->clearIdentityMapForEntityName($entityName);
2390 3
            $this->clearEntityInsertionsForEntityName($entityName);
2391
        }
2392
2393 1218
        if ($this->evm->hasListeners(Events::onClear)) {
2394 7
            $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em, $entityName));
2395
        }
2396 1218
    }
2397
2398
    /**
2399
     * INTERNAL:
2400
     * Schedules an orphaned entity for removal. The remove() operation will be
2401
     * invoked on that entity at the beginning of the next commit of this
2402
     * UnitOfWork.
2403
     *
2404
     * @ignore
2405
     *
2406
     * @param object $entity
2407
     *
2408
     * @return void
2409
     */
2410 17
    public function scheduleOrphanRemoval($entity)
2411
    {
2412 17
        $this->orphanRemovals[spl_object_hash($entity)] = $entity;
2413 17
    }
2414
2415
    /**
2416
     * INTERNAL:
2417
     * Cancels a previously scheduled orphan removal.
2418
     *
2419
     * @ignore
2420
     *
2421
     * @param object $entity
2422
     *
2423
     * @return void
2424
     */
2425 112
    public function cancelOrphanRemoval($entity)
2426
    {
2427 112
        unset($this->orphanRemovals[spl_object_hash($entity)]);
2428 112
    }
2429
2430
    /**
2431
     * INTERNAL:
2432
     * Schedules a complete collection for removal when this UnitOfWork commits.
2433
     *
2434
     * @param PersistentCollection $coll
2435
     *
2436
     * @return void
2437
     */
2438 13
    public function scheduleCollectionDeletion(PersistentCollection $coll)
2439
    {
2440 13
        $coid = spl_object_hash($coll);
2441
2442
        // TODO: if $coll is already scheduled for recreation ... what to do?
2443
        // Just remove $coll from the scheduled recreations?
2444 13
        unset($this->collectionUpdates[$coid]);
2445
2446 13
        $this->collectionDeletions[$coid] = $coll;
2447 13
    }
2448
2449
    /**
2450
     * @param PersistentCollection $coll
2451
     *
2452
     * @return bool
2453
     */
2454
    public function isCollectionScheduledForDeletion(PersistentCollection $coll)
2455
    {
2456
        return isset($this->collectionDeletions[spl_object_hash($coll)]);
2457
    }
2458
2459
    /**
2460
     * @param ClassMetadata $class
2461
     *
2462
     * @return \Doctrine\Common\Persistence\ObjectManagerAware|object
2463
     */
2464 669
    private function newInstance($class)
2465
    {
2466 669
        $entity = $class->newInstance();
2467
2468 669
        if ($entity instanceof \Doctrine\Common\Persistence\ObjectManagerAware) {
2469 4
            $entity->injectObjectManager($this->em, $class);
2470
        }
2471
2472 669
        return $entity;
2473
    }
2474
2475
    /**
2476
     * INTERNAL:
2477
     * Creates an entity. Used for reconstitution of persistent entities.
2478
     *
2479
     * Internal note: Highly performance-sensitive method.
2480
     *
2481
     * @ignore
2482
     *
2483
     * @param string $className The name of the entity class.
2484
     * @param array  $data      The data for the entity.
2485
     * @param array  $hints     Any hints to account for during reconstitution/lookup of the entity.
2486
     *
2487
     * @return object The managed entity instance.
2488
     *
2489
     * @todo Rename: getOrCreateEntity
2490
     */
2491 808
    public function createEntity($className, array $data, &$hints = array())
2492
    {
2493 808
        $class = $this->em->getClassMetadata($className);
2494
        //$isReadOnly = isset($hints[Query::HINT_READ_ONLY]);
2495
2496 808
        $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...
2497 808
        $idHash = implode(' ', $id);
2498
2499 808
        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...
2500 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...
2501 312
            $oid = spl_object_hash($entity);
2502
2503
            if (
2504 312
                isset($hints[Query::HINT_REFRESH])
2505 312
                && isset($hints[Query::HINT_REFRESH_ENTITY])
2506 312
                && ($unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY]) !== $entity
2507 312
                && $unmanagedProxy instanceof Proxy
2508 312
                && $this->isIdentifierEquals($unmanagedProxy, $entity)
2509
            ) {
2510
                // DDC-1238 - we have a managed instance, but it isn't the provided one.
2511
                // Therefore we clear its identifier. Also, we must re-fetch metadata since the
2512
                // refreshed object may be anything
2513
2514 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...
2515 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...
2516
                }
2517
2518 2
                return $unmanagedProxy;
2519
            }
2520
2521 310
            if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
2522 22
                $entity->__setInitialized(true);
2523
2524 22
                $overrideLocalValues = true;
2525
2526 22
                if ($entity instanceof NotifyPropertyChanged) {
2527 22
                    $entity->addPropertyChangedListener($this);
2528
                }
2529
            } else {
2530 290
                $overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
2531
2532
                // If only a specific entity is set to refresh, check that it's the one
2533 290
                if (isset($hints[Query::HINT_REFRESH_ENTITY])) {
2534 71
                    $overrideLocalValues = $hints[Query::HINT_REFRESH_ENTITY] === $entity;
2535
                }
2536
            }
2537
2538 310
            if ($overrideLocalValues) {
2539
                // inject ObjectManager upon refresh.
2540 113
                if ($entity instanceof ObjectManagerAware) {
2541 3
                    $entity->injectObjectManager($this->em, $class);
2542
                }
2543
2544 310
                $this->originalEntityData[$oid] = $data;
2545
            }
2546
        } else {
2547 666
            $entity = $this->newInstance($class);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

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

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

Loading history...
2548 666
            $oid    = spl_object_hash($entity);
2549
2550 666
            $this->entityIdentifiers[$oid]  = $id;
2551 666
            $this->entityStates[$oid]       = self::STATE_MANAGED;
2552 666
            $this->originalEntityData[$oid] = $data;
2553
2554 666
            $this->identityMap[$class->rootEntityName][$idHash] = $entity;
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2555
2556 666
            if ($entity instanceof NotifyPropertyChanged) {
2557 2
                $entity->addPropertyChangedListener($this);
2558
            }
2559
2560 666
            $overrideLocalValues = true;
2561
        }
2562
2563 807
        if ( ! $overrideLocalValues) {
2564 220
            return $entity;
2565
        }
2566
2567 704
        foreach ($data as $field => $value) {
2568 704
            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...
2569 704
                $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...
2570
            }
2571
        }
2572
2573
        // Loading the entity right here, if its in the eager loading map get rid of it there.
2574 704
        unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
2575
2576 704
        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...
2577
            unset($this->eagerLoadingEntities[$class->rootEntityName]);
2578
        }
2579
2580
        // Properly initialize any unfetched associations, if partial objects are not allowed.
2581 704
        if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
2582 33
            return $entity;
2583
        }
2584
2585 671
        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...
2586
            // Check if the association is not among the fetch-joined associations already.
2587 587
            if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) {
2588 260
                continue;
2589
            }
2590
2591 565
            $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
2592
2593
            switch (true) {
2594 565
                case ($assoc['type'] & ClassMetadata::TO_ONE):
2595 485
                    if ( ! $assoc['isOwningSide']) {
2596
2597
                        // use the given entity association
2598 64
                        if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2599
2600 2
                            $this->originalEntityData[$oid][$field] = $data[$field];
2601
2602 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...
2603 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...
2604
2605 2
                            continue 2;
2606
                        }
2607
2608
                        // Inverse side of x-to-one can never be lazy
2609 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...
2610
2611 62
                        continue 2;
2612
                    }
2613
2614
                    // use the entity association
2615 485
                    if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2616 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...
2617 38
                        $this->originalEntityData[$oid][$field] = $data[$field];
2618
2619 38
                        continue;
2620
                    }
2621
2622 478
                    $associatedId = array();
2623
2624
                    // TODO: Is this even computed right in all cases of composite keys?
2625 478
                    foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
2626 478
                        $joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
2627
2628 478
                        if ($joinColumnValue !== null) {
2629 283
                            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...
2630 11
                                $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
2631
                            } else {
2632 283
                                $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...
2633
                            }
2634 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...
2635 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...
2636
                        ) {
2637
                            // the missing key is part of target's entity primary key
2638 7
                            $associatedId = array();
2639 478
                            break;
2640
                        }
2641
                    }
2642
2643 478
                    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...
2644
                        // Foreign key is NULL
2645 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...
2646 287
                        $this->originalEntityData[$oid][$field] = null;
2647
2648 287
                        continue;
2649
                    }
2650
2651 283
                    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...
2652 280
                        $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...
2653
                    }
2654
2655
                    // Foreign key is set
2656
                    // Check identity map first
2657
                    // FIXME: Can break easily with composite keys if join column values are in
2658
                    //        wrong order. The correct order is the one in ClassMetadata#identifier.
2659 283
                    $relatedIdHash = implode(' ', $associatedId);
2660
2661
                    switch (true) {
2662 283
                        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...
2663 165
                            $newValue = $this->identityMap[$targetClass->rootEntityName][$relatedIdHash];
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2664
2665
                            // If this is an uninitialized proxy, we are deferring eager loads,
2666
                            // this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
2667
                            // then we can append this entity for eager loading!
2668 165
                            if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER &&
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2669 165
                                isset($hints[self::HINT_DEFEREAGERLOAD]) &&
2670 165
                                !$targetClass->isIdentifierComposite &&
0 ignored issues
show
Bug introduced by
Accessing isIdentifierComposite on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2671 165
                                $newValue instanceof Proxy &&
2672 165
                                $newValue->__isInitialized__ === false) {
0 ignored issues
show
Bug introduced by
Accessing __isInitialized__ on the interface Doctrine\ORM\Proxy\Proxy suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2673
2674
                                $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...
2675
                            }
2676
2677 165
                            break;
2678
2679 192
                        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...
2680
                            // If it might be a subtype, it can not be lazy. There isn't even
2681
                            // a way to solve this with deferred eager loading, which means putting
2682
                            // an entity with subclasses at a *-to-one location is really bad! (performance-wise)
2683 30
                            $newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, $associatedId);
2684 30
                            break;
2685
2686
                        default:
2687
                            switch (true) {
2688
                                // We are negating the condition here. Other cases will assume it is valid!
2689 163
                                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...
2690 156
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2691 156
                                    break;
2692
2693
                                // Deferred eager load only works for single identifier classes
2694 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...
2695
                                    // TODO: Is there a faster approach?
2696 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...
2697
2698 7
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2699 7
                                    break;
2700
2701
                                default:
2702
                                    // TODO: This is very imperformant, ignore it?
2703
                                    $newValue = $this->em->find($assoc['targetEntity'], $associatedId);
2704
                                    break;
2705
                            }
2706
2707
                            // PERF: Inlined & optimized code from UnitOfWork#registerManaged()
2708 163
                            $newValueOid = spl_object_hash($newValue);
2709 163
                            $this->entityIdentifiers[$newValueOid] = $associatedId;
2710 163
                            $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...
2711
2712
                            if (
2713 163
                                $newValue instanceof NotifyPropertyChanged &&
2714 163
                                ( ! $newValue instanceof Proxy || $newValue->__isInitialized())
2715
                            ) {
2716
                                $newValue->addPropertyChangedListener($this);
2717
                            }
2718 163
                            $this->entityStates[$newValueOid] = self::STATE_MANAGED;
2719
                            // make sure that when an proxy is then finally loaded, $this->originalEntityData is set also!
2720 163
                            break;
2721
                    }
2722
2723 283
                    $this->originalEntityData[$oid][$field] = $newValue;
2724 283
                    $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...
2725
2726 283
                    if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
2727 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...
2728 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...
2729
                    }
2730
2731 283
                    break;
2732
2733
                default:
2734
                    // Ignore if its a cached collection
2735 487
                    if (isset($hints[Query::HINT_CACHE_ENABLED]) && $class->getFieldValue($entity, $field) instanceof PersistentCollection) {
2736
                        break;
2737
                    }
2738
2739
                    // use the given collection
2740 487
                    if (isset($data[$field]) && $data[$field] instanceof PersistentCollection) {
2741
2742 3
                        $data[$field]->setOwner($entity, $assoc);
2743
2744 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...
2745 3
                        $this->originalEntityData[$oid][$field] = $data[$field];
2746
2747 3
                        break;
2748
                    }
2749
2750
                    // Inject collection
2751 487
                    $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...
2752 487
                    $pColl->setOwner($entity, $assoc);
2753 487
                    $pColl->setInitialized(false);
2754
2755 487
                    $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...
2756 487
                    $reflField->setValue($entity, $pColl);
2757
2758 487
                    if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
2759 4
                        $this->loadCollection($pColl);
2760 4
                        $pColl->takeSnapshot();
2761
                    }
2762
2763 487
                    $this->originalEntityData[$oid][$field] = $pColl;
2764 565
                    break;
2765
            }
2766
        }
2767
2768 671
        if ($overrideLocalValues) {
2769
            // defer invoking of postLoad event to hydration complete step
2770 671
            $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...
2771
        }
2772
2773 671
        return $entity;
2774
    }
2775
2776
    /**
2777
     * @return void
2778
     */
2779 864
    public function triggerEagerLoads()
2780
    {
2781 864
        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...
2782 864
            return;
2783
        }
2784
2785
        // avoid infinite recursion
2786 7
        $eagerLoadingEntities       = $this->eagerLoadingEntities;
2787 7
        $this->eagerLoadingEntities = array();
2788
2789 7
        foreach ($eagerLoadingEntities as $entityName => $ids) {
2790 7
            if ( ! $ids) {
2791
                continue;
2792
            }
2793
2794 7
            $class = $this->em->getClassMetadata($entityName);
2795
2796 7
            $this->getEntityPersister($entityName)->loadAll(
2797 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...
2798
            );
2799
        }
2800 7
    }
2801
2802
    /**
2803
     * Initializes (loads) an uninitialized persistent collection of an entity.
2804
     *
2805
     * @param \Doctrine\ORM\PersistentCollection $collection The collection to initialize.
2806
     *
2807
     * @return void
2808
     *
2809
     * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733.
2810
     */
2811 143
    public function loadCollection(PersistentCollection $collection)
2812
    {
2813 143
        $assoc     = $collection->getMapping();
2814 143
        $persister = $this->getEntityPersister($assoc['targetEntity']);
2815
2816 143
        switch ($assoc['type']) {
2817 143
            case ClassMetadata::ONE_TO_MANY:
2818 76
                $persister->loadOneToManyCollection($assoc, $collection->getOwner(), $collection);
2819 76
                break;
2820
2821 81
            case ClassMetadata::MANY_TO_MANY:
2822 81
                $persister->loadManyToManyCollection($assoc, $collection->getOwner(), $collection);
2823 81
                break;
2824
        }
2825
2826 143
        $collection->setInitialized(true);
2827 143
    }
2828
2829
    /**
2830
     * Gets the identity map of the UnitOfWork.
2831
     *
2832
     * @return array
2833
     */
2834 2
    public function getIdentityMap()
2835
    {
2836 2
        return $this->identityMap;
2837
    }
2838
2839
    /**
2840
     * Gets the original data of an entity. The original data is the data that was
2841
     * present at the time the entity was reconstituted from the database.
2842
     *
2843
     * @param object $entity
2844
     *
2845
     * @return array
2846
     */
2847 115
    public function getOriginalEntityData($entity)
2848
    {
2849 115
        $oid = spl_object_hash($entity);
2850
2851 115
        return isset($this->originalEntityData[$oid])
2852 112
            ? $this->originalEntityData[$oid]
2853 115
            : [];
2854
    }
2855
2856
    /**
2857
     * @ignore
2858
     *
2859
     * @param object $entity
2860
     * @param array  $data
2861
     *
2862
     * @return void
2863
     */
2864
    public function setOriginalEntityData($entity, array $data)
2865
    {
2866
        $this->originalEntityData[spl_object_hash($entity)] = $data;
2867
    }
2868
2869
    /**
2870
     * INTERNAL:
2871
     * Sets a property value of the original data array of an entity.
2872
     *
2873
     * @ignore
2874
     *
2875
     * @param string $oid
2876
     * @param string $property
2877
     * @param mixed  $value
2878
     *
2879
     * @return void
2880
     */
2881 313
    public function setOriginalEntityProperty($oid, $property, $value)
2882
    {
2883 313
        $this->originalEntityData[$oid][$property] = $value;
2884 313
    }
2885
2886
    /**
2887
     * Gets the identifier of an entity.
2888
     * The returned value is always an array of identifier values. If the entity
2889
     * has a composite identifier then the identifier values are in the same
2890
     * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames().
2891
     *
2892
     * @param object $entity
2893
     *
2894
     * @return array The identifier values.
2895
     */
2896 843
    public function getEntityIdentifier($entity)
2897
    {
2898 843
        return $this->entityIdentifiers[spl_object_hash($entity)];
2899
    }
2900
2901
    /**
2902
     * Processes an entity instance to extract their identifier values.
2903
     *
2904
     * @param object $entity The entity instance.
2905
     *
2906
     * @return mixed A scalar value.
2907
     *
2908
     * @throws \Doctrine\ORM\ORMInvalidArgumentException
2909
     */
2910 126
    public function getSingleIdentifierValue($entity)
2911
    {
2912 126
        $class = $this->em->getClassMetadata(get_class($entity));
2913
2914 126
        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...
2915
            throw ORMInvalidArgumentException::invalidCompositeIdentifier();
2916
        }
2917
2918 126
        $values = $this->isInIdentityMap($entity)
2919 113
            ? $this->getEntityIdentifier($entity)
2920 126
            : $class->getIdentifierValues($entity);
2921
2922 126
        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...
2923
    }
2924
2925
    /**
2926
     * Tries to find an entity with the given identifier in the identity map of
2927
     * this UnitOfWork.
2928
     *
2929
     * @param mixed  $id            The entity identifier to look for.
2930
     * @param string $rootClassName The name of the root class of the mapped entity hierarchy.
2931
     *
2932
     * @return object|bool Returns the entity with the specified identifier if it exists in
2933
     *                     this UnitOfWork, FALSE otherwise.
2934
     */
2935 522
    public function tryGetById($id, $rootClassName)
2936
    {
2937 522
        $idHash = implode(' ', (array) $id);
2938
2939 522
        return isset($this->identityMap[$rootClassName][$idHash])
2940 79
            ? $this->identityMap[$rootClassName][$idHash]
2941 522
            : false;
2942
    }
2943
2944
    /**
2945
     * Schedules an entity for dirty-checking at commit-time.
2946
     *
2947
     * @param object $entity The entity to schedule for dirty-checking.
2948
     *
2949
     * @return void
2950
     *
2951
     * @todo Rename: scheduleForSynchronization
2952
     */
2953 5
    public function scheduleForDirtyCheck($entity)
2954
    {
2955 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...
2956
2957 5
        $this->scheduledForSynchronization[$rootClassName][spl_object_hash($entity)] = $entity;
2958 5
    }
2959
2960
    /**
2961
     * Checks whether the UnitOfWork has any pending insertions.
2962
     *
2963
     * @return boolean TRUE if this UnitOfWork has pending insertions, FALSE otherwise.
2964
     */
2965
    public function hasPendingInsertions()
2966
    {
2967
        return ! empty($this->entityInsertions);
2968
    }
2969
2970
    /**
2971
     * Calculates the size of the UnitOfWork. The size of the UnitOfWork is the
2972
     * number of entities in the identity map.
2973
     *
2974
     * @return integer
2975
     */
2976 1
    public function size()
2977
    {
2978 1
        $countArray = array_map('count', $this->identityMap);
2979
2980 1
        return array_sum($countArray);
2981
    }
2982
2983
    /**
2984
     * Gets the EntityPersister for an Entity.
2985
     *
2986
     * @param string $entityName The name of the Entity.
2987
     *
2988
     * @return \Doctrine\ORM\Persisters\Entity\EntityPersister
2989
     */
2990 1069
    public function getEntityPersister($entityName)
2991
    {
2992 1069
        if (isset($this->persisters[$entityName])) {
2993 846
            return $this->persisters[$entityName];
2994
        }
2995
2996 1069
        $class = $this->em->getClassMetadata($entityName);
2997
2998
        switch (true) {
2999 1069
            case ($class->isInheritanceTypeNone()):
3000 1032
                $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...
3001 1032
                break;
3002
3003 359
            case ($class->isInheritanceTypeSingleTable()):
3004 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...
3005 213
                break;
3006
3007 338
            case ($class->isInheritanceTypeJoined()):
3008 338
                $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...
3009 338
                break;
3010
3011
            default:
3012
                throw new \RuntimeException('No persister found for entity.');
3013
        }
3014
3015 1069
        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...
3016 119
            $persister = $this->em->getConfiguration()
3017 119
                ->getSecondLevelCacheConfiguration()
3018 119
                ->getCacheFactory()
3019 119
                ->buildCachedEntityPersister($this->em, $persister, $class);
3020
        }
3021
3022 1069
        $this->persisters[$entityName] = $persister;
3023
3024 1069
        return $this->persisters[$entityName];
3025
    }
3026
3027
    /**
3028
     * Gets a collection persister for a collection-valued association.
3029
     *
3030
     * @param array $association
3031
     *
3032
     * @return \Doctrine\ORM\Persisters\Collection\CollectionPersister
3033
     */
3034 569
    public function getCollectionPersister(array $association)
3035
    {
3036 569
        $role = isset($association['cache'])
3037 76
            ? $association['sourceEntity'] . '::' . $association['fieldName']
3038 569
            : $association['type'];
3039
3040 569
        if (isset($this->collectionPersisters[$role])) {
3041 448
            return $this->collectionPersisters[$role];
3042
        }
3043
3044 569
        $persister = ClassMetadata::ONE_TO_MANY === $association['type']
3045 403
            ? new OneToManyPersister($this->em)
3046 569
            : new ManyToManyPersister($this->em);
3047
3048 569
        if ($this->hasCache && isset($association['cache'])) {
3049 75
            $persister = $this->em->getConfiguration()
3050 75
                ->getSecondLevelCacheConfiguration()
3051 75
                ->getCacheFactory()
3052 75
                ->buildCachedCollectionPersister($this->em, $persister, $association);
3053
        }
3054
3055 569
        $this->collectionPersisters[$role] = $persister;
3056
3057 569
        return $this->collectionPersisters[$role];
3058
    }
3059
3060
    /**
3061
     * INTERNAL:
3062
     * Registers an entity as managed.
3063
     *
3064
     * @param object $entity The entity.
3065
     * @param array  $id     The identifier values.
3066
     * @param array  $data   The original entity data.
3067
     *
3068
     * @return void
3069
     */
3070 206
    public function registerManaged($entity, array $id, array $data)
3071
    {
3072 206
        $oid = spl_object_hash($entity);
3073
3074 206
        $this->entityIdentifiers[$oid]  = $id;
3075 206
        $this->entityStates[$oid]       = self::STATE_MANAGED;
3076 206
        $this->originalEntityData[$oid] = $data;
3077
3078 206
        $this->addToIdentityMap($entity);
3079
3080 200
        if ($entity instanceof NotifyPropertyChanged && ( ! $entity instanceof Proxy || $entity->__isInitialized())) {
3081 2
            $entity->addPropertyChangedListener($this);
3082
        }
3083 200
    }
3084
3085
    /**
3086
     * INTERNAL:
3087
     * Clears the property changeset of the entity with the given OID.
3088
     *
3089
     * @param string $oid The entity's OID.
3090
     *
3091
     * @return void
3092
     */
3093
    public function clearEntityChangeSet($oid)
3094
    {
3095
        $this->entityChangeSets[$oid] = array();
3096
    }
3097
3098
    /* PropertyChangedListener implementation */
3099
3100
    /**
3101
     * Notifies this UnitOfWork of a property change in an entity.
3102
     *
3103
     * @param object $entity       The entity that owns the property.
3104
     * @param string $propertyName The name of the property that changed.
3105
     * @param mixed  $oldValue     The old value of the property.
3106
     * @param mixed  $newValue     The new value of the property.
3107
     *
3108
     * @return void
3109
     */
3110 3
    public function propertyChanged($entity, $propertyName, $oldValue, $newValue)
3111
    {
3112 3
        $oid   = spl_object_hash($entity);
3113 3
        $class = $this->em->getClassMetadata(get_class($entity));
3114
3115 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...
3116
3117 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...
3118 1
            return; // ignore non-persistent fields
3119
        }
3120
3121
        // Update changeset and mark entity for synchronization
3122 3
        $this->entityChangeSets[$oid][$propertyName] = array($oldValue, $newValue);
3123
3124 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...
3125 3
            $this->scheduleForDirtyCheck($entity);
3126
        }
3127 3
    }
3128
3129
    /**
3130
     * Gets the currently scheduled entity insertions in this UnitOfWork.
3131
     *
3132
     * @return array
3133
     */
3134 2
    public function getScheduledEntityInsertions()
3135
    {
3136 2
        return $this->entityInsertions;
3137
    }
3138
3139
    /**
3140
     * Gets the currently scheduled entity updates in this UnitOfWork.
3141
     *
3142
     * @return array
3143
     */
3144 2
    public function getScheduledEntityUpdates()
3145
    {
3146 2
        return $this->entityUpdates;
3147
    }
3148
3149
    /**
3150
     * Gets the currently scheduled entity deletions in this UnitOfWork.
3151
     *
3152
     * @return array
3153
     */
3154 1
    public function getScheduledEntityDeletions()
3155
    {
3156 1
        return $this->entityDeletions;
3157
    }
3158
3159
    /**
3160
     * Gets the currently scheduled complete collection deletions
3161
     *
3162
     * @return array
3163
     */
3164 1
    public function getScheduledCollectionDeletions()
3165
    {
3166 1
        return $this->collectionDeletions;
3167
    }
3168
3169
    /**
3170
     * Gets the currently scheduled collection inserts, updates and deletes.
3171
     *
3172
     * @return array
3173
     */
3174
    public function getScheduledCollectionUpdates()
3175
    {
3176
        return $this->collectionUpdates;
3177
    }
3178
3179
    /**
3180
     * Helper method to initialize a lazy loading proxy or persistent collection.
3181
     *
3182
     * @param object $obj
3183
     *
3184
     * @return void
3185
     */
3186 2
    public function initializeObject($obj)
3187
    {
3188 2
        if ($obj instanceof Proxy) {
3189 1
            $obj->__load();
3190
3191 1
            return;
3192
        }
3193
3194 1
        if ($obj instanceof PersistentCollection) {
3195 1
            $obj->initialize();
3196
        }
3197 1
    }
3198
3199
    /**
3200
     * Helper method to show an object as string.
3201
     *
3202
     * @param object $obj
3203
     *
3204
     * @return string
3205
     */
3206 1
    private static function objToStr($obj)
3207
    {
3208 1
        return method_exists($obj, '__toString') ? (string) $obj : get_class($obj).'@'.spl_object_hash($obj);
3209
    }
3210
3211
    /**
3212
     * Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit().
3213
     *
3214
     * This operation cannot be undone as some parts of the UnitOfWork now keep gathering information
3215
     * on this object that might be necessary to perform a correct update.
3216
     *
3217
     * @param object $object
3218
     *
3219
     * @return void
3220
     *
3221
     * @throws ORMInvalidArgumentException
3222
     */
3223 6
    public function markReadOnly($object)
3224
    {
3225 6
        if ( ! is_object($object) || ! $this->isInIdentityMap($object)) {
3226 1
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3227
        }
3228
3229 5
        $this->readOnlyObjects[spl_object_hash($object)] = true;
3230 5
    }
3231
3232
    /**
3233
     * Is this entity read only?
3234
     *
3235
     * @param object $object
3236
     *
3237
     * @return bool
3238
     *
3239
     * @throws ORMInvalidArgumentException
3240
     */
3241 3
    public function isReadOnly($object)
3242
    {
3243 3
        if ( ! is_object($object)) {
3244
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3245
        }
3246
3247 3
        return isset($this->readOnlyObjects[spl_object_hash($object)]);
3248
    }
3249
3250
    /**
3251
     * Perform whatever processing is encapsulated here after completion of the transaction.
3252
     */
3253 1002
    private function afterTransactionComplete()
3254
    {
3255
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3256 89
            $persister->afterTransactionComplete();
3257 1002
        });
3258 1002
    }
3259
3260
    /**
3261
     * Perform whatever processing is encapsulated here after completion of the rolled-back.
3262
     */
3263
    private function afterTransactionRolledBack()
3264
    {
3265 11
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3266 3
            $persister->afterTransactionRolledBack();
3267 11
        });
3268 11
    }
3269
3270
    /**
3271
     * Performs an action after the transaction.
3272
     *
3273
     * @param callable $callback
3274
     */
3275 1007
    private function performCallbackOnCachedPersister(callable $callback)
3276
    {
3277 1007
        if ( ! $this->hasCache) {
3278 918
            return;
3279
        }
3280
3281 89
        foreach (array_merge($this->persisters, $this->collectionPersisters) as $persister) {
3282 89
            if ($persister instanceof CachedPersister) {
3283 89
                $callback($persister);
3284
            }
3285
        }
3286 89
    }
3287
3288 1011
    private function dispatchOnFlushEvent()
3289
    {
3290 1011
        if ($this->evm->hasListeners(Events::onFlush)) {
3291 4
            $this->evm->dispatchEvent(Events::onFlush, new OnFlushEventArgs($this->em));
3292
        }
3293 1011
    }
3294
3295 1006
    private function dispatchPostFlushEvent()
3296
    {
3297 1006
        if ($this->evm->hasListeners(Events::postFlush)) {
3298 5
            $this->evm->dispatchEvent(Events::postFlush, new PostFlushEventArgs($this->em));
3299
        }
3300 1005
    }
3301
3302
    /**
3303
     * Verifies if two given entities actually are the same based on identifier comparison
3304
     *
3305
     * @param object $entity1
3306
     * @param object $entity2
3307
     *
3308
     * @return bool
3309
     */
3310 14
    private function isIdentifierEquals($entity1, $entity2)
3311
    {
3312 14
        if ($entity1 === $entity2) {
3313
            return true;
3314
        }
3315
3316 14
        $class = $this->em->getClassMetadata(get_class($entity1));
3317
3318 14
        if ($class !== $this->em->getClassMetadata(get_class($entity2))) {
3319 11
            return false;
3320
        }
3321
3322 3
        $oid1 = spl_object_hash($entity1);
3323 3
        $oid2 = spl_object_hash($entity2);
3324
3325 3
        $id1 = isset($this->entityIdentifiers[$oid1])
3326 3
            ? $this->entityIdentifiers[$oid1]
3327 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...
3328 3
        $id2 = isset($this->entityIdentifiers[$oid2])
3329 3
            ? $this->entityIdentifiers[$oid2]
3330 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...
3331
3332 3
        return $id1 === $id2 || implode(' ', $id1) === implode(' ', $id2);
3333
    }
3334
3335
    /**
3336
     * @param object $entity
3337
     * @param object $managedCopy
3338
     *
3339
     * @throws ORMException
3340
     * @throws OptimisticLockException
3341
     * @throws TransactionRequiredException
3342
     */
3343 30
    private function mergeEntityStateIntoManagedCopy($entity, $managedCopy)
3344
    {
3345 30
        $class = $this->em->getClassMetadata(get_class($entity));
3346
3347 30
        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...
3348 30
            $name = $prop->name;
3349
3350 30
            $prop->setAccessible(true);
3351
3352 30
            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...
3353 30
                if ( ! $class->isIdentifier($name)) {
3354 30
                    $prop->setValue($managedCopy, $prop->getValue($entity));
3355
                }
3356
            } else {
3357 28
                $assoc2 = $class->associationMappings[$name];
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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