Completed
Pull Request — master (#5866)
by Šimon
09:22
created

UnitOfWork::getEntityIdentifier()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
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 2324
    public function __construct(EntityManagerInterface $em)
293
    {
294 2324
        $this->em                         = $em;
295 2324
        $this->evm                        = $em->getEventManager();
296 2324
        $this->listenersInvoker           = new ListenersInvoker($em);
297 2324
        $this->hasCache                   = $em->getConfiguration()->isSecondLevelCacheEnabled();
298 2324
        $this->identifierFlattener        = new IdentifierFlattener($this, $em->getMetadataFactory());
299 2324
        $this->hydrationCompleteHandler   = new HydrationCompleteHandler($this->listenersInvoker, $em);
300 2324
        $this->reflectionPropertiesGetter = new ReflectionPropertiesGetter(new RuntimeReflectionService());
301 2324
    }
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 1012
    public function commit($entity = null)
323
    {
324
        // Raise preFlush
325 1012
        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 1012
        if ($entity === null) {
331 1004
            $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 1009
        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 165
                $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 1009
                $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 1005
        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 1005
        $this->dispatchOnFlushEvent();
359
360
        // Now we need a commit order to maintain referential integrity
361 1005
        $commitOrder = $this->getCommitOrder();
362
363 1005
        $conn = $this->em->getConnection();
364 1005
        $conn->beginTransaction();
365
366
        try {
367
            // Collection deletions (deletions of complete collections)
368 1005
            foreach ($this->collectionDeletions as $collectionToDelete) {
369 19
                $this->getCollectionPersister($collectionToDelete->getMapping())->delete($collectionToDelete);
370
            }
371
372 1005
            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 1001
                foreach ($commitOrder as $class) {
374 1001
                    $this->executeInserts($class);
375
                }
376
            }
377
378 1004
            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 1000
            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 1000
            foreach ($this->collectionUpdates as $collectionToUpdate) {
391 529
                $this->getCollectionPersister($collectionToUpdate->getMapping())->update($collectionToUpdate);
392
            }
393
394
            // Entity deletions come last and need to be in reverse commit order
395 1000
            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 62
                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 62
                    $this->executeDeletions($commitOrder[$i]);
398
                }
399
            }
400
401 1000
            $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 1000
        $this->afterTransactionComplete();
412
413
        // Take new snapshots from visited collections
414 1000
        foreach ($this->visitedCollections as $coll) {
415 528
            $coll->takeSnapshot();
416
        }
417
418 1000
        $this->dispatchPostFlushEvent();
419
420
        // Clear up
421 999
        $this->entityInsertions =
422 999
        $this->entityUpdates =
423 999
        $this->entityDeletions =
424 999
        $this->extraUpdates =
425 999
        $this->entityChangeSets =
426 999
        $this->collectionUpdates =
427 999
        $this->collectionDeletions =
428 999
        $this->visitedCollections =
429 999
        $this->scheduledForSynchronization =
430 999
        $this->orphanRemovals = array();
431
432 999
        $this->dispatchAfterFlush();
433 999
    }
434
435
    /**
436
     * Computes the changesets of all entities scheduled for insertion.
437
     *
438
     * @return void
439
     */
440 1011
    private function computeScheduleInsertsChangeSets()
441
    {
442 1011
        foreach ($this->entityInsertions as $entity) {
443 1003
            $class = $this->em->getClassMetadata(get_class($entity));
444
445 1003
            $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...
446
        }
447 1009
    }
448
449
    /**
450
     * Only flushes the given entity according to a ruleset that keeps the UoW consistent.
451
     *
452
     * 1. All entities scheduled for insertion, (orphan) removals and changes in collections are processed as well!
453
     * 2. Read Only entities are skipped.
454
     * 3. Proxies are skipped.
455
     * 4. Only if entity is properly managed.
456
     *
457
     * @param object $entity
458
     *
459
     * @return void
460
     *
461
     * @throws \InvalidArgumentException
462
     */
463 16
    private function computeSingleEntityChangeSet($entity)
464
    {
465 16
        $state = $this->getEntityState($entity);
466
467 16
        if ($state !== self::STATE_MANAGED && $state !== self::STATE_REMOVED) {
468 1
            throw new \InvalidArgumentException("Entity has to be managed or scheduled for removal for single computation " . self::objToStr($entity));
469
        }
470
471 15
        $class = $this->em->getClassMetadata(get_class($entity));
472
473 15
        if ($state === self::STATE_MANAGED && $class->isChangeTrackingDeferredImplicit()) {
474 14
            $this->persist($entity);
475
        }
476
477
        // Compute changes for INSERTed entities first. This must always happen even in this case.
478 15
        $this->computeScheduleInsertsChangeSets();
479
480 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...
481
            return;
482
        }
483
484
        // Ignore uninitialized proxy objects
485 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...
486 2
            return;
487
        }
488
489
        // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here.
490 13
        $oid = spl_object_hash($entity);
491
492 13
        if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) {
493 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...
494
        }
495 12
    }
496
497
    /**
498
     * Executes any extra updates that have been scheduled.
499
     */
500 40
    private function executeExtraUpdates()
501
    {
502 40
        foreach ($this->extraUpdates as $oid => $update) {
503 40
            list ($entity, $changeset) = $update;
504
505 40
            $this->entityChangeSets[$oid] = $changeset;
506 40
            $this->getEntityPersister(get_class($entity))->update($entity);
507
        }
508
509 40
        $this->extraUpdates = array();
510 40
    }
511
512
    /**
513
     * Gets the changeset for an entity.
514
     *
515
     * @param object $entity
516
     *
517
     * @return array
518
     */
519 1003
    public function & getEntityChangeSet($entity)
520
    {
521 1003
        $oid  = spl_object_hash($entity);
522 1003
        $data = array();
523
524 1003
        if (!isset($this->entityChangeSets[$oid])) {
525 1
            return $data;
526
        }
527
528 1003
        return $this->entityChangeSets[$oid];
529
    }
530
531
    /**
532
     * Computes the changes that happened to a single entity.
533
     *
534
     * Modifies/populates the following properties:
535
     *
536
     * {@link _originalEntityData}
537
     * If the entity is NEW or MANAGED but not yet fully persisted (only has an id)
538
     * then it was not fetched from the database and therefore we have no original
539
     * entity data yet. All of the current entity data is stored as the original entity data.
540
     *
541
     * {@link _entityChangeSets}
542
     * The changes detected on all properties of the entity are stored there.
543
     * A change is a tuple array where the first entry is the old value and the second
544
     * entry is the new value of the property. Changesets are used by persisters
545
     * to INSERT/UPDATE the persistent entity state.
546
     *
547
     * {@link _entityUpdates}
548
     * If the entity is already fully MANAGED (has been fetched from the database before)
549
     * and any changes to its properties are detected, then a reference to the entity is stored
550
     * there to mark it for an update.
551
     *
552
     * {@link _collectionDeletions}
553
     * If a PersistentCollection has been de-referenced in a fully MANAGED entity,
554
     * then this collection is marked for deletion.
555
     *
556
     * @ignore
557
     *
558
     * @internal Don't call from the outside.
559
     *
560
     * @param ClassMetadata $class  The class descriptor of the entity.
561
     * @param object        $entity The entity for which to compute the changes.
562
     *
563
     * @return void
564
     */
565 1013
    public function computeChangeSet(ClassMetadata $class, $entity)
566
    {
567 1013
        $oid = spl_object_hash($entity);
568
569 1013
        if (isset($this->readOnlyObjects[$oid])) {
570 2
            return;
571
        }
572
573 1013
        if ( ! $class->isInheritanceTypeNone()) {
574 306
            $class = $this->em->getClassMetadata(get_class($entity));
575
        }
576
577 1013
        $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...
578
579 1013
        if ($invoke !== ListenersInvoker::INVOKE_NONE) {
580 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...
581
        }
582
583 1013
        $actualData = array();
584
585 1013
        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...
586 1013
            $value = $refProp->getValue($entity);
587
588 1013
            if ($class->isCollectionValuedAssociation($name) && $value !== null) {
589 775
                if ($value instanceof PersistentCollection) {
590 198
                    if ($value->getOwner() === $entity) {
591 198
                        continue;
592
                    }
593
594 5
                    $value = new ArrayCollection($value->getValues());
595
                }
596
597
                // If $value is not a Collection then use an ArrayCollection.
598 770
                if ( ! $value instanceof Collection) {
599 242
                    $value = new ArrayCollection($value);
600
                }
601
602 770
                $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...
603
604
                // Inject PersistentCollection
605 770
                $value = new PersistentCollection(
606 770
                    $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...
607
                );
608 770
                $value->setOwner($entity, $assoc);
609 770
                $value->setDirty( ! $value->isEmpty());
610
611 770
                $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...
612
613 770
                $actualData[$name] = $value;
614
615 770
                continue;
616
            }
617
618 1013
            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...
619 1013
                $actualData[$name] = $value;
620
            }
621
        }
622
623 1013
        if ( ! isset($this->originalEntityData[$oid])) {
624
            // Entity is either NEW or MANAGED but not yet fully persisted (only has an id).
625
            // These result in an INSERT.
626 1009
            $this->originalEntityData[$oid] = $actualData;
627 1009
            $changeSet = array();
628
629 1009
            foreach ($actualData as $propName => $actualValue) {
630 994
                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...
631 943
                    $changeSet[$propName] = array(null, $actualValue);
632
633 943
                    continue;
634
                }
635
636 892
                $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...
637
638 892
                if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
639 892
                    $changeSet[$propName] = array(null, $actualValue);
640
                }
641
            }
642
643 1009
            $this->entityChangeSets[$oid] = $changeSet;
644
        } else {
645
            // Entity is "fully" MANAGED: it was already fully persisted before
646
            // and we have a copy of the original data
647 262
            $originalData           = $this->originalEntityData[$oid];
648 262
            $isChangeTrackingNotify = $class->isChangeTrackingNotify();
649 262
            $changeSet              = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid]))
650
                ? $this->entityChangeSets[$oid]
651 262
                : array();
652
653 262
            foreach ($actualData as $propName => $actualValue) {
654
                // skip field, its a partially omitted one!
655 247
                if ( ! (isset($originalData[$propName]) || array_key_exists($propName, $originalData))) {
656 7
                    continue;
657
                }
658
659 247
                $orgValue = $originalData[$propName];
660
661
                // skip if value haven't changed
662 247
                if ($orgValue === $actualValue) {
663 231
                    continue;
664
                }
665
666
                // if regular field
667 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...
668 58
                    if ($isChangeTrackingNotify) {
669
                        continue;
670
                    }
671
672 58
                    $changeSet[$propName] = array($orgValue, $actualValue);
673
674 58
                    continue;
675
                }
676
677 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...
678
679
                // Persistent collection was exchanged with the "originally"
680
                // created one. This can only mean it was cloned and replaced
681
                // on another entity.
682 58
                if ($actualValue instanceof PersistentCollection) {
683 8
                    $owner = $actualValue->getOwner();
684 8
                    if ($owner === null) { // cloned
685
                        $actualValue->setOwner($entity, $assoc);
686 8
                    } else if ($owner !== $entity) { // no clone, we have to fix
687
                        if (!$actualValue->isInitialized()) {
688
                            $actualValue->initialize(); // we have to do this otherwise the cols share state
689
                        }
690
                        $newValue = clone $actualValue;
691
                        $newValue->setOwner($entity, $assoc);
692
                        $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...
693
                    }
694
                }
695
696 58
                if ($orgValue instanceof PersistentCollection) {
697
                    // A PersistentCollection was de-referenced, so delete it.
698 8
                    $coid = spl_object_hash($orgValue);
699
700 8
                    if (isset($this->collectionDeletions[$coid])) {
701
                        continue;
702
                    }
703
704 8
                    $this->collectionDeletions[$coid] = $orgValue;
705 8
                    $changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored.
706
707 8
                    continue;
708
                }
709
710 50
                if ($assoc['type'] & ClassMetadata::TO_ONE) {
711 49
                    if ($assoc['isOwningSide']) {
712 21
                        $changeSet[$propName] = array($orgValue, $actualValue);
713
                    }
714
715 49
                    if ($orgValue !== null && $assoc['orphanRemoval']) {
716 50
                        $this->scheduleOrphanRemoval($orgValue);
717
                    }
718
                }
719
            }
720
721 262
            if ($changeSet) {
722 85
                $this->entityChangeSets[$oid]   = $changeSet;
723 85
                $this->originalEntityData[$oid] = $actualData;
724 85
                $this->entityUpdates[$oid]      = $entity;
725
            }
726
        }
727
728
        // Look for changes in associations of the entity
729 1013
        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...
730 892
            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...
731 638
                continue;
732
            }
733
734 863
            $this->computeAssociationChanges($assoc, $val);
735
736 855
            if ( ! isset($this->entityChangeSets[$oid]) &&
737 855
                $assoc['isOwningSide'] &&
738 855
                $assoc['type'] == ClassMetadata::MANY_TO_MANY &&
739 855
                $val instanceof PersistentCollection &&
740 855
                $val->isDirty()) {
741
742 35
                $this->entityChangeSets[$oid]   = array();
743 35
                $this->originalEntityData[$oid] = $actualData;
744 855
                $this->entityUpdates[$oid]      = $entity;
745
            }
746
        }
747 1005
    }
748
749
    /**
750
     * Computes all the changes that have been done to entities and collections
751
     * since the last commit and stores these changes in the _entityChangeSet map
752
     * temporarily for access by the persisters, until the UoW commit is finished.
753
     *
754
     * @return void
755
     */
756 1004
    public function computeChangeSets()
757
    {
758
        // Compute changes for INSERTed entities first. This must always happen.
759 1004
        $this->computeScheduleInsertsChangeSets();
760
761
        // Compute changes for other MANAGED entities. Change tracking policies take effect here.
762 1002
        foreach ($this->identityMap as $className => $entities) {
763 444
            $class = $this->em->getClassMetadata($className);
764
765
            // Skip class if instances are read-only
766 444
            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...
767 1
                continue;
768
            }
769
770
            // If change tracking is explicit or happens through notification, then only compute
771
            // changes on entities of that type that are explicitly marked for synchronization.
772
            switch (true) {
773 443
                case ($class->isChangeTrackingDeferredImplicit()):
774 441
                    $entitiesToProcess = $entities;
775 441
                    break;
776
777 3
                case (isset($this->scheduledForSynchronization[$className])):
778 3
                    $entitiesToProcess = $this->scheduledForSynchronization[$className];
779 3
                    break;
780
781
                default:
782 1
                    $entitiesToProcess = array();
783
784
            }
785
786 443
            foreach ($entitiesToProcess as $entity) {
787
                // Ignore uninitialized proxy objects
788 424
                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...
789 34
                    continue;
790
                }
791
792
                // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here.
793 423
                $oid = spl_object_hash($entity);
794
795 423
                if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) {
796 443
                    $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...
797
                }
798
            }
799
        }
800 1002
    }
801
802
    /**
803
     * Computes the changes of an association.
804
     *
805
     * @param array $assoc The association mapping.
806
     * @param mixed $value The value of the association.
807
     *
808
     * @throws ORMInvalidArgumentException
809
     * @throws ORMException
810
     *
811
     * @return void
812
     */
813 863
    private function computeAssociationChanges($assoc, $value)
814
    {
815 863
        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...
816 27
            return;
817
        }
818
819 862
        if ($value instanceof PersistentCollection && $value->isDirty()) {
820 531
            $coid = spl_object_hash($value);
821
822 531
            $this->collectionUpdates[$coid] = $value;
823 531
            $this->visitedCollections[$coid] = $value;
824
        }
825
826
        // Look through the entities, and in any of their associations,
827
        // for transient (new) entities, recursively. ("Persistence by reachability")
828
        // Unwrap. Uninitialized collections will simply be empty.
829 862
        $unwrappedValue = ($assoc['type'] & ClassMetadata::TO_ONE) ? array($value) : $value->unwrap();
830 862
        $targetClass    = $this->em->getClassMetadata($assoc['targetEntity']);
831
832 862
        foreach ($unwrappedValue as $key => $entry) {
833 720
            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...
834 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...
835
            }
836
837 714
            $state = $this->getEntityState($entry, self::STATE_NEW);
838
839 714
            if ( ! ($entry instanceof $assoc['targetEntity'])) {
840
                throw ORMException::unexpectedAssociationValue($assoc['sourceEntity'], $assoc['fieldName'], get_class($entry), $assoc['targetEntity']);
841
            }
842
843
            switch ($state) {
844 714
                case self::STATE_NEW:
845 39
                    if ( ! $assoc['isCascadePersist']) {
846 4
                        throw ORMInvalidArgumentException::newEntityFoundThroughRelationship($assoc, $entry);
847
                    }
848
849 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...
850 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...
851 35
                    break;
852
853 708
                case self::STATE_REMOVED:
854
                    // Consume the $value as array (it's either an array or an ArrayAccess)
855
                    // and remove the element from Collection.
856 4
                    if ($assoc['type'] & ClassMetadata::TO_MANY) {
857 3
                        unset($value[$key]);
858
                    }
859 4
                    break;
860
861 708
                case self::STATE_DETACHED:
862
                    // Can actually not happen right now as we assume STATE_NEW,
863
                    // so the exception will be raised from the DBAL layer (constraint violation).
864
                    throw ORMInvalidArgumentException::detachedEntityFoundThroughRelationship($assoc, $entry);
865
                    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...
866
867 711
                default:
868
                    // MANAGED associated entities are already taken into account
869
                    // during changeset calculation anyway, since they are in the identity map.
870
            }
871
        }
872 854
    }
873
874
    /**
875
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
876
     * @param object                              $entity
877
     *
878
     * @return void
879
     */
880 1022
    private function persistNew($class, $entity)
881
    {
882 1022
        $oid    = spl_object_hash($entity);
883 1022
        $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::prePersist);
884
885 1022
        if ($invoke !== ListenersInvoker::INVOKE_NONE) {
886 139
            $this->listenersInvoker->invoke($class, Events::prePersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
887
        }
888
889 1022
        $idGen = $class->idGenerator;
890
891 1022
        if ( ! $idGen->isPostInsertGenerator()) {
892 261
            $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...
893
894 261
            if ( ! $idGen instanceof \Doctrine\ORM\Id\AssignedGenerator) {
895 1
                $idValue = array($class->identifier[0] => $idValue);
896
897 1
                $class->setIdentifierValues($entity, $idValue);
898
            }
899
900 261
            $this->entityIdentifiers[$oid] = $idValue;
901
        }
902
903 1022
        $this->entityStates[$oid] = self::STATE_MANAGED;
904
905 1022
        $this->scheduleForInsert($entity);
906 1022
    }
907
908
    /**
909
     * INTERNAL:
910
     * Computes the changeset of an individual entity, independently of the
911
     * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit().
912
     *
913
     * The passed entity must be a managed entity. If the entity already has a change set
914
     * because this method is invoked during a commit cycle then the change sets are added.
915
     * whereby changes detected in this method prevail.
916
     *
917
     * @ignore
918
     *
919
     * @param ClassMetadata $class  The class descriptor of the entity.
920
     * @param object        $entity The entity for which to (re)calculate the change set.
921
     *
922
     * @return void
923
     *
924
     * @throws ORMInvalidArgumentException If the passed entity is not MANAGED.
925
     */
926 16
    public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity)
927
    {
928 16
        $oid = spl_object_hash($entity);
929
930 16
        if ( ! isset($this->entityStates[$oid]) || $this->entityStates[$oid] != self::STATE_MANAGED) {
931
            throw ORMInvalidArgumentException::entityNotManaged($entity);
932
        }
933
934
        // skip if change tracking is "NOTIFY"
935 16
        if ($class->isChangeTrackingNotify()) {
936
            return;
937
        }
938
939 16
        if ( ! $class->isInheritanceTypeNone()) {
940 3
            $class = $this->em->getClassMetadata(get_class($entity));
941
        }
942
943 16
        $actualData = array();
944
945 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...
946 16
            if (( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity())
947 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...
948 16
                && ! $class->isCollectionValuedAssociation($name)) {
949 16
                $actualData[$name] = $refProp->getValue($entity);
950
            }
951
        }
952
953 16
        if ( ! isset($this->originalEntityData[$oid])) {
954
            throw new \RuntimeException('Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.');
955
        }
956
957 16
        $originalData = $this->originalEntityData[$oid];
958 16
        $changeSet = array();
959
960 16
        foreach ($actualData as $propName => $actualValue) {
961 16
            $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
962
963 16
            if ($orgValue !== $actualValue) {
964 16
                $changeSet[$propName] = array($orgValue, $actualValue);
965
            }
966
        }
967
968 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...
969 7
            if (isset($this->entityChangeSets[$oid])) {
970 6
                $this->entityChangeSets[$oid] = array_merge($this->entityChangeSets[$oid], $changeSet);
971 1
            } else if ( ! isset($this->entityInsertions[$oid])) {
972 1
                $this->entityChangeSets[$oid] = $changeSet;
973 1
                $this->entityUpdates[$oid]    = $entity;
974
            }
975 7
            $this->originalEntityData[$oid] = $actualData;
976
        }
977 16
    }
978
979
    /**
980
     * Executes all entity insertions for entities of the specified type.
981
     *
982
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
983
     *
984
     * @return void
985
     */
986 1001
    private function executeInserts($class)
987
    {
988 1001
        $entities   = array();
989 1001
        $className  = $class->name;
990 1001
        $persister  = $this->getEntityPersister($className);
991 1001
        $invoke     = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist);
992
993 1001
        foreach ($this->entityInsertions as $oid => $entity) {
994
995 1001
            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...
996 853
                continue;
997
            }
998
999 1001
            $persister->addInsert($entity);
1000
1001 1001
            unset($this->entityInsertions[$oid]);
1002
1003 1001
            if ($invoke !== ListenersInvoker::INVOKE_NONE) {
1004 1001
                $entities[] = $entity;
1005
            }
1006
        }
1007
1008 1001
        $postInsertIds = $persister->executeInserts();
1009
1010 1001
        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...
1011
            // Persister returned post-insert IDs
1012 918
            foreach ($postInsertIds as $postInsertId) {
1013 918
                $id      = $postInsertId['generatedId'];
1014 918
                $entity  = $postInsertId['entity'];
1015 918
                $oid     = spl_object_hash($entity);
1016 918
                $idField = $class->identifier[0];
1017
1018 918
                $class->reflFields[$idField]->setValue($entity, $id);
1019
1020 918
                $this->entityIdentifiers[$oid] = array($idField => $id);
1021 918
                $this->entityStates[$oid] = self::STATE_MANAGED;
1022 918
                $this->originalEntityData[$oid][$idField] = $id;
1023
1024 918
                $this->addToIdentityMap($entity);
1025
            }
1026
        }
1027
1028 1001
        foreach ($entities as $entity) {
1029 135
            $this->listenersInvoker->invoke($class, Events::postPersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
1030
        }
1031 1001
    }
1032
1033
    /**
1034
     * Executes all entity updates for entities of the specified type.
1035
     *
1036
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
1037
     *
1038
     * @return void
1039
     */
1040 116
    private function executeUpdates($class)
1041
    {
1042 116
        $className          = $class->name;
1043 116
        $persister          = $this->getEntityPersister($className);
1044 116
        $preUpdateInvoke    = $this->listenersInvoker->getSubscribedSystems($class, Events::preUpdate);
1045 116
        $postUpdateInvoke   = $this->listenersInvoker->getSubscribedSystems($class, Events::postUpdate);
1046
1047 116
        foreach ($this->entityUpdates as $oid => $entity) {
1048 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...
1049 74
                continue;
1050
            }
1051
1052 116
            if ($preUpdateInvoke != ListenersInvoker::INVOKE_NONE) {
1053 13
                $this->listenersInvoker->invoke($class, Events::preUpdate, $entity, new PreUpdateEventArgs($entity, $this->em, $this->getEntityChangeSet($entity)), $preUpdateInvoke);
1054
1055 13
                $this->recomputeSingleEntityChangeSet($class, $entity);
1056
            }
1057
1058 116
            if ( ! empty($this->entityChangeSets[$oid])) {
1059 82
                $persister->update($entity);
1060
            }
1061
1062 112
            unset($this->entityUpdates[$oid]);
1063
1064 112
            if ($postUpdateInvoke != ListenersInvoker::INVOKE_NONE) {
1065 112
                $this->listenersInvoker->invoke($class, Events::postUpdate, $entity, new LifecycleEventArgs($entity, $this->em), $postUpdateInvoke);
1066
            }
1067
        }
1068 112
    }
1069
1070
    /**
1071
     * Executes all entity deletions for entities of the specified type.
1072
     *
1073
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
1074
     *
1075
     * @return void
1076
     */
1077 62
    private function executeDeletions($class)
1078
    {
1079 62
        $className  = $class->name;
1080 62
        $persister  = $this->getEntityPersister($className);
1081 62
        $invoke     = $this->listenersInvoker->getSubscribedSystems($class, Events::postRemove);
1082
1083 62
        foreach ($this->entityDeletions as $oid => $entity) {
1084 62
            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...
1085 26
                continue;
1086
            }
1087
1088 62
            $persister->delete($entity);
1089
1090
            unset(
1091 62
                $this->entityDeletions[$oid],
1092 62
                $this->entityIdentifiers[$oid],
1093 62
                $this->originalEntityData[$oid],
1094 62
                $this->entityStates[$oid]
1095
            );
1096
1097
            // Entity with this $oid after deletion treated as NEW, even if the $oid
1098
            // is obtained by a new entity because the old one went out of scope.
1099
            //$this->entityStates[$oid] = self::STATE_NEW;
1100 62
            if ( ! $class->isIdentifierNatural()) {
1101 52
                $class->reflFields[$class->identifier[0]]->setValue($entity, null);
1102
            }
1103
1104 62
            if ($invoke !== ListenersInvoker::INVOKE_NONE) {
1105 62
                $this->listenersInvoker->invoke($class, Events::postRemove, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
1106
            }
1107
        }
1108 61
    }
1109
1110
    /**
1111
     * Gets the commit order.
1112
     *
1113
     * @param array|null $entityChangeSet
1114
     *
1115
     * @return array
1116
     */
1117 1005
    private function getCommitOrder(array $entityChangeSet = null)
1118
    {
1119 1005
        if ($entityChangeSet === null) {
1120 1005
            $entityChangeSet = array_merge($this->entityInsertions, $this->entityUpdates, $this->entityDeletions);
1121
        }
1122
1123 1005
        $calc = $this->getCommitOrderCalculator();
1124
1125
        // See if there are any new classes in the changeset, that are not in the
1126
        // commit order graph yet (don't have a node).
1127
        // We have to inspect changeSet to be able to correctly build dependencies.
1128
        // It is not possible to use IdentityMap here because post inserted ids
1129
        // are not yet available.
1130 1005
        $newNodes = array();
1131
1132 1005
        foreach ($entityChangeSet as $entity) {
1133 1005
            $class = $this->em->getClassMetadata(get_class($entity));
1134
1135 1005
            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...
1136 623
                continue;
1137
            }
1138
1139 1005
            $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...
1140
1141 1005
            $newNodes[] = $class;
1142
        }
1143
1144
        // Calculate dependencies for new nodes
1145 1005
        while ($class = array_pop($newNodes)) {
1146 1005
            foreach ($class->associationMappings as $assoc) {
1147 884
                if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) {
1148 844
                    continue;
1149
                }
1150
1151 837
                $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
1152
1153 837
                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...
1154 648
                    $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...
1155
1156 648
                    $newNodes[] = $targetClass;
1157
                }
1158
1159 837
                $joinColumns = reset($assoc['joinColumns']);
1160 837
                $isNullable  = isset($joinColumns['nullable']) ? $joinColumns['nullable'] : false;
1161
1162 837
                $calc->addDependency($targetClass->name, $class->name, $isNullable ? 0 : 1);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1163
1164
                // If the target class has mapped subclasses, these share the same dependency.
1165 837
                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...
1166 830
                    continue;
1167
                }
1168
1169 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...
1170 217
                    $targetSubClass = $this->em->getClassMetadata($subClassName);
1171
1172 217
                    if ( ! $calc->hasNode($subClassName)) {
1173 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...
1174
1175 189
                        $newNodes[] = $targetSubClass;
1176
                    }
1177
1178 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...
1179
                }
1180
            }
1181
        }
1182
1183 1005
        return $calc->sort();
1184
    }
1185
1186
    /**
1187
     * Schedules an entity for insertion into the database.
1188
     * If the entity already has an identifier, it will be added to the identity map.
1189
     *
1190
     * @param object $entity The entity to schedule for insertion.
1191
     *
1192
     * @return void
1193
     *
1194
     * @throws ORMInvalidArgumentException
1195
     * @throws \InvalidArgumentException
1196
     */
1197 1023
    public function scheduleForInsert($entity)
1198
    {
1199 1023
        $oid = spl_object_hash($entity);
1200
1201 1023
        if (isset($this->entityUpdates[$oid])) {
1202
            throw new InvalidArgumentException("Dirty entity can not be scheduled for insertion.");
1203
        }
1204
1205 1023
        if (isset($this->entityDeletions[$oid])) {
1206 1
            throw ORMInvalidArgumentException::scheduleInsertForRemovedEntity($entity);
1207
        }
1208 1023
        if (isset($this->originalEntityData[$oid]) && ! isset($this->entityInsertions[$oid])) {
1209 1
            throw ORMInvalidArgumentException::scheduleInsertForManagedEntity($entity);
1210
        }
1211
1212 1023
        if (isset($this->entityInsertions[$oid])) {
1213 1
            throw ORMInvalidArgumentException::scheduleInsertTwice($entity);
1214
        }
1215
1216 1023
        $this->entityInsertions[$oid] = $entity;
1217
1218 1023
        if (isset($this->entityIdentifiers[$oid])) {
1219 261
            $this->addToIdentityMap($entity);
1220
        }
1221
1222 1023
        if ($entity instanceof NotifyPropertyChanged) {
1223 5
            $entity->addPropertyChangedListener($this);
1224
        }
1225 1023
    }
1226
1227
    /**
1228
     * Checks whether an entity is scheduled for insertion.
1229
     *
1230
     * @param object $entity
1231
     *
1232
     * @return boolean
1233
     */
1234 629
    public function isScheduledForInsert($entity)
1235
    {
1236 629
        return isset($this->entityInsertions[spl_object_hash($entity)]);
1237
    }
1238
1239
    /**
1240
     * Schedules an entity for being updated.
1241
     *
1242
     * @param object $entity The entity to schedule for being updated.
1243
     *
1244
     * @return void
1245
     *
1246
     * @throws ORMInvalidArgumentException
1247
     */
1248 1
    public function scheduleForUpdate($entity)
1249
    {
1250 1
        $oid = spl_object_hash($entity);
1251
1252 1
        if ( ! isset($this->entityIdentifiers[$oid])) {
1253
            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "scheduling for update");
1254
        }
1255
1256 1
        if (isset($this->entityDeletions[$oid])) {
1257
            throw ORMInvalidArgumentException::entityIsRemoved($entity, "schedule for update");
1258
        }
1259
1260 1
        if ( ! isset($this->entityUpdates[$oid]) && ! isset($this->entityInsertions[$oid])) {
1261 1
            $this->entityUpdates[$oid] = $entity;
1262
        }
1263 1
    }
1264
1265
    /**
1266
     * INTERNAL:
1267
     * Schedules an extra update that will be executed immediately after the
1268
     * regular entity updates within the currently running commit cycle.
1269
     *
1270
     * Extra updates for entities are stored as (entity, changeset) tuples.
1271
     *
1272
     * @ignore
1273
     *
1274
     * @param object $entity    The entity for which to schedule an extra update.
1275
     * @param array  $changeset The changeset of the entity (what to update).
1276
     *
1277
     * @return void
1278
     */
1279 40
    public function scheduleExtraUpdate($entity, array $changeset)
1280
    {
1281 40
        $oid         = spl_object_hash($entity);
1282 40
        $extraUpdate = array($entity, $changeset);
1283
1284 40
        if (isset($this->extraUpdates[$oid])) {
1285 1
            list(, $changeset2) = $this->extraUpdates[$oid];
1286
1287 1
            $extraUpdate = array($entity, $changeset + $changeset2);
1288
        }
1289
1290 40
        $this->extraUpdates[$oid] = $extraUpdate;
1291 40
    }
1292
1293
    /**
1294
     * Checks whether an entity is registered as dirty in the unit of work.
1295
     * Note: Is not very useful currently as dirty entities are only registered
1296
     * at commit time.
1297
     *
1298
     * @param object $entity
1299
     *
1300
     * @return boolean
1301
     */
1302
    public function isScheduledForUpdate($entity)
1303
    {
1304
        return isset($this->entityUpdates[spl_object_hash($entity)]);
1305
    }
1306
1307
    /**
1308
     * Checks whether an entity is registered to be checked in the unit of work.
1309
     *
1310
     * @param object $entity
1311
     *
1312
     * @return boolean
1313
     */
1314 1
    public function isScheduledForDirtyCheck($entity)
1315
    {
1316 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...
1317
1318 1
        return isset($this->scheduledForSynchronization[$rootEntityName][spl_object_hash($entity)]);
1319
    }
1320
1321
    /**
1322
     * INTERNAL:
1323
     * Schedules an entity for deletion.
1324
     *
1325
     * @param object $entity
1326
     *
1327
     * @return void
1328
     */
1329 65
    public function scheduleForDelete($entity)
1330
    {
1331 65
        $oid = spl_object_hash($entity);
1332
1333 65
        if (isset($this->entityInsertions[$oid])) {
1334 1
            if ($this->isInIdentityMap($entity)) {
1335
                $this->removeFromIdentityMap($entity);
1336
            }
1337
1338 1
            unset($this->entityInsertions[$oid], $this->entityStates[$oid]);
1339
1340 1
            return; // entity has not been persisted yet, so nothing more to do.
1341
        }
1342
1343 65
        if ( ! $this->isInIdentityMap($entity)) {
1344 1
            return;
1345
        }
1346
1347 64
        $this->removeFromIdentityMap($entity);
1348
1349 64
        if (isset($this->entityUpdates[$oid])) {
1350 1
            unset($this->entityUpdates[$oid]);
1351
        }
1352
1353 64
        if ( ! isset($this->entityDeletions[$oid])) {
1354 64
            $this->entityDeletions[$oid] = $entity;
1355 64
            $this->entityStates[$oid]    = self::STATE_REMOVED;
1356
        }
1357 64
    }
1358
1359
    /**
1360
     * Checks whether an entity is registered as removed/deleted with the unit
1361
     * of work.
1362
     *
1363
     * @param object $entity
1364
     *
1365
     * @return boolean
1366
     */
1367 17
    public function isScheduledForDelete($entity)
1368
    {
1369 17
        return isset($this->entityDeletions[spl_object_hash($entity)]);
1370
    }
1371
1372
    /**
1373
     * Checks whether an entity is scheduled for insertion, update or deletion.
1374
     *
1375
     * @param object $entity
1376
     *
1377
     * @return boolean
1378
     */
1379
    public function isEntityScheduled($entity)
1380
    {
1381
        $oid = spl_object_hash($entity);
1382
1383
        return isset($this->entityInsertions[$oid])
1384
            || isset($this->entityUpdates[$oid])
1385
            || isset($this->entityDeletions[$oid]);
1386
    }
1387
1388
    /**
1389
     * INTERNAL:
1390
     * Registers an entity in the identity map.
1391
     * Note that entities in a hierarchy are registered with the class name of
1392
     * the root entity.
1393
     *
1394
     * @ignore
1395
     *
1396
     * @param object $entity The entity to register.
1397
     *
1398
     * @return boolean TRUE if the registration was successful, FALSE if the identity of
1399
     *                 the entity in question is already managed.
1400
     *
1401
     * @throws ORMInvalidArgumentException
1402
     */
1403 1084
    public function addToIdentityMap($entity)
1404
    {
1405 1084
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1406 1084
        $idHash        = implode(' ', $this->entityIdentifiers[spl_object_hash($entity)]);
1407
1408 1084
        if ($idHash === '') {
1409 1
            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...
1410
        }
1411
1412 1083
        $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...
1413
1414 1083
        if (isset($this->identityMap[$className][$idHash])) {
1415 74
            return false;
1416
        }
1417
1418 1083
        $this->identityMap[$className][$idHash] = $entity;
1419
1420 1083
        return true;
1421
    }
1422
1423
    /**
1424
     * Gets the state of an entity with regard to the current unit of work.
1425
     *
1426
     * @param object   $entity
1427
     * @param int|null $assume The state to assume if the state is not yet known (not MANAGED or REMOVED).
1428
     *                         This parameter can be set to improve performance of entity state detection
1429
     *                         by potentially avoiding a database lookup if the distinction between NEW and DETACHED
1430
     *                         is either known or does not matter for the caller of the method.
1431
     *
1432
     * @return int The entity state.
1433
     */
1434 1036
    public function getEntityState($entity, $assume = null)
1435
    {
1436 1036
        $oid = spl_object_hash($entity);
1437
1438 1036
        if (isset($this->entityStates[$oid])) {
1439 780
            return $this->entityStates[$oid];
1440
        }
1441
1442 1030
        if ($assume !== null) {
1443 1026
            return $assume;
1444
        }
1445
1446
        // State can only be NEW or DETACHED, because MANAGED/REMOVED states are known.
1447
        // Note that you can not remember the NEW or DETACHED state in _entityStates since
1448
        // the UoW does not hold references to such objects and the object hash can be reused.
1449
        // More generally because the state may "change" between NEW/DETACHED without the UoW being aware of it.
1450 13
        $class = $this->em->getClassMetadata(get_class($entity));
1451 13
        $id    = $class->getIdentifierValues($entity);
1452
1453 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...
1454 5
            return self::STATE_NEW;
1455
        }
1456
1457 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...
1458 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...
1459
        }
1460
1461
        switch (true) {
1462 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...
1463
                // Check for a version field, if available, to avoid a db lookup.
1464 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...
1465 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...
1466
                        ? self::STATE_DETACHED
1467 1
                        : self::STATE_NEW;
1468
                }
1469
1470
                // Last try before db lookup: check the identity map.
1471 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...
1472 1
                    return self::STATE_DETACHED;
1473
                }
1474
1475
                // db lookup
1476 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...
1477
                    return self::STATE_DETACHED;
1478
                }
1479
1480 4
                return self::STATE_NEW;
1481
1482 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...
1483
                // if we have a pre insert generator we can't be sure that having an id
1484
                // really means that the entity exists. We have to verify this through
1485
                // the last resort: a db lookup
1486
1487
                // Last try before db lookup: check the identity map.
1488
                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...
1489
                    return self::STATE_DETACHED;
1490
                }
1491
1492
                // db lookup
1493
                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...
1494
                    return self::STATE_DETACHED;
1495
                }
1496
1497
                return self::STATE_NEW;
1498
1499
            default:
1500 5
                return self::STATE_DETACHED;
1501
        }
1502
    }
1503
1504
    /**
1505
     * INTERNAL:
1506
     * Removes an entity from the identity map. This effectively detaches the
1507
     * entity from the persistence management of Doctrine.
1508
     *
1509
     * @ignore
1510
     *
1511
     * @param object $entity
1512
     *
1513
     * @return boolean
1514
     *
1515
     * @throws ORMInvalidArgumentException
1516
     */
1517 76
    public function removeFromIdentityMap($entity)
1518
    {
1519 76
        $oid           = spl_object_hash($entity);
1520 76
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1521 76
        $idHash        = implode(' ', $this->entityIdentifiers[$oid]);
1522
1523 76
        if ($idHash === '') {
1524
            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "remove from identity map");
1525
        }
1526
1527 76
        $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...
1528
1529 76
        if (isset($this->identityMap[$className][$idHash])) {
1530 76
            unset($this->identityMap[$className][$idHash]);
1531 76
            unset($this->readOnlyObjects[$oid]);
1532
1533
            //$this->entityStates[$oid] = self::STATE_DETACHED;
1534
1535 76
            return true;
1536
        }
1537
1538
        return false;
1539
    }
1540
1541
    /**
1542
     * INTERNAL:
1543
     * Gets an entity in the identity map by its identifier hash.
1544
     *
1545
     * @ignore
1546
     *
1547
     * @param string $idHash
1548
     * @param string $rootClassName
1549
     *
1550
     * @return object
1551
     */
1552
    public function getByIdHash($idHash, $rootClassName)
1553
    {
1554
        return $this->identityMap[$rootClassName][$idHash];
1555
    }
1556
1557
    /**
1558
     * INTERNAL:
1559
     * Tries to get an entity by its identifier hash. If no entity is found for
1560
     * the given hash, FALSE is returned.
1561
     *
1562
     * @ignore
1563
     *
1564
     * @param mixed  $idHash        (must be possible to cast it to string)
1565
     * @param string $rootClassName
1566
     *
1567
     * @return object|bool The found entity or FALSE.
1568
     */
1569 34
    public function tryGetByIdHash($idHash, $rootClassName)
1570
    {
1571 34
        $stringIdHash = (string) $idHash;
1572
1573 34
        if (isset($this->identityMap[$rootClassName][$stringIdHash])) {
1574 34
            return $this->identityMap[$rootClassName][$stringIdHash];
1575
        }
1576
1577 2
        return false;
1578
    }
1579
1580
    /**
1581
     * Checks whether an entity is registered in the identity map of this UnitOfWork.
1582
     *
1583
     * @param object $entity
1584
     *
1585
     * @return boolean
1586
     */
1587 212
    public function isInIdentityMap($entity)
1588
    {
1589 212
        $oid = spl_object_hash($entity);
1590
1591 212
        if ( ! isset($this->entityIdentifiers[$oid])) {
1592 31
            return false;
1593
        }
1594
1595 197
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1596 197
        $idHash        = implode(' ', $this->entityIdentifiers[$oid]);
1597
1598 197
        if ($idHash === '') {
1599
            return false;
1600
        }
1601
1602 197
        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...
1603
    }
1604
1605
    /**
1606
     * INTERNAL:
1607
     * Checks whether an identifier hash exists in the identity map.
1608
     *
1609
     * @ignore
1610
     *
1611
     * @param string $idHash
1612
     * @param string $rootClassName
1613
     *
1614
     * @return boolean
1615
     */
1616
    public function containsIdHash($idHash, $rootClassName)
1617
    {
1618
        return isset($this->identityMap[$rootClassName][$idHash]);
1619
    }
1620
1621
    /**
1622
     * Persists an entity as part of the current unit of work.
1623
     *
1624
     * @param object $entity The entity to persist.
1625
     *
1626
     * @return void
1627
     */
1628 1019
    public function persist($entity)
1629
    {
1630 1019
        $visited = array();
1631
1632 1019
        $this->doPersist($entity, $visited);
1633 1012
    }
1634
1635
    /**
1636
     * Persists an entity as part of the current unit of work.
1637
     *
1638
     * This method is internally called during persist() cascades as it tracks
1639
     * the already visited entities to prevent infinite recursions.
1640
     *
1641
     * @param object $entity  The entity to persist.
1642
     * @param array  $visited The already visited entities.
1643
     *
1644
     * @return void
1645
     *
1646
     * @throws ORMInvalidArgumentException
1647
     * @throws UnexpectedValueException
1648
     */
1649 1019
    private function doPersist($entity, array &$visited)
1650
    {
1651 1019
        $oid = spl_object_hash($entity);
1652
1653 1019
        if (isset($visited[$oid])) {
1654 109
            return; // Prevent infinite recursion
1655
        }
1656
1657 1019
        $visited[$oid] = $entity; // Mark visited
1658
1659 1019
        $class = $this->em->getClassMetadata(get_class($entity));
1660
1661
        // We assume NEW, so DETACHED entities result in an exception on flush (constraint violation).
1662
        // If we would detect DETACHED here we would throw an exception anyway with the same
1663
        // consequences (not recoverable/programming error), so just assuming NEW here
1664
        // lets us avoid some database lookups for entities with natural identifiers.
1665 1019
        $entityState = $this->getEntityState($entity, self::STATE_NEW);
1666
1667
        switch ($entityState) {
1668 1019
            case self::STATE_MANAGED:
1669
                // Nothing to do, except if policy is "deferred explicit"
1670 234
                if ($class->isChangeTrackingDeferredExplicit()) {
1671 2
                    $this->scheduleForDirtyCheck($entity);
1672
                }
1673 234
                break;
1674
1675 1019
            case self::STATE_NEW:
1676 1018
                $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...
1677 1018
                break;
1678
1679 1
            case self::STATE_REMOVED:
1680
                // Entity becomes managed again
1681 1
                unset($this->entityDeletions[$oid]);
1682 1
                $this->addToIdentityMap($entity);
1683
1684 1
                $this->entityStates[$oid] = self::STATE_MANAGED;
1685 1
                break;
1686
1687
            case self::STATE_DETACHED:
1688
                // Can actually not happen right now since we assume STATE_NEW.
1689
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "persisted");
1690
1691
            default:
1692
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1693
        }
1694
1695 1019
        $this->cascadePersist($entity, $visited);
1696 1012
    }
1697
1698
    /**
1699
     * Deletes an entity as part of the current unit of work.
1700
     *
1701
     * @param object $entity The entity to remove.
1702
     *
1703
     * @return void
1704
     */
1705 64
    public function remove($entity)
1706
    {
1707 64
        $visited = array();
1708
1709 64
        $this->doRemove($entity, $visited);
1710 64
    }
1711
1712
    /**
1713
     * Deletes an entity as part of the current unit of work.
1714
     *
1715
     * This method is internally called during delete() cascades as it tracks
1716
     * the already visited entities to prevent infinite recursions.
1717
     *
1718
     * @param object $entity  The entity to delete.
1719
     * @param array  $visited The map of the already visited entities.
1720
     *
1721
     * @return void
1722
     *
1723
     * @throws ORMInvalidArgumentException If the instance is a detached entity.
1724
     * @throws UnexpectedValueException
1725
     */
1726 64
    private function doRemove($entity, array &$visited)
1727
    {
1728 64
        $oid = spl_object_hash($entity);
1729
1730 64
        if (isset($visited[$oid])) {
1731 1
            return; // Prevent infinite recursion
1732
        }
1733
1734 64
        $visited[$oid] = $entity; // mark visited
1735
1736
        // Cascade first, because scheduleForDelete() removes the entity from the identity map, which
1737
        // can cause problems when a lazy proxy has to be initialized for the cascade operation.
1738 64
        $this->cascadeRemove($entity, $visited);
1739
1740 64
        $class       = $this->em->getClassMetadata(get_class($entity));
1741 64
        $entityState = $this->getEntityState($entity);
1742
1743
        switch ($entityState) {
1744 64
            case self::STATE_NEW:
1745 64
            case self::STATE_REMOVED:
1746
                // nothing to do
1747 2
                break;
1748
1749 64
            case self::STATE_MANAGED:
1750 64
                $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...
1751
1752 64
                if ($invoke !== ListenersInvoker::INVOKE_NONE) {
1753 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...
1754
                }
1755
1756 64
                $this->scheduleForDelete($entity);
1757 64
                break;
1758
1759
            case self::STATE_DETACHED:
1760
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "removed");
1761
            default:
1762
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1763
        }
1764
1765 64
    }
1766
1767
    /**
1768
     * Merges the state of the given detached entity into this UnitOfWork.
1769
     *
1770
     * @param object $entity
1771
     *
1772
     * @return object The managed copy of the entity.
1773
     *
1774
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1775
     *         attribute and the version check against the managed copy fails.
1776
     *
1777
     * @todo Require active transaction!? OptimisticLockException may result in undefined state!?
1778
     */
1779 40
    public function merge($entity)
1780
    {
1781 40
        $visited = array();
1782
1783 40
        return $this->doMerge($entity, $visited);
1784
    }
1785
1786
    /**
1787
     * Executes a merge operation on an entity.
1788
     *
1789
     * @param object      $entity
1790
     * @param array       $visited
1791
     * @param object|null $prevManagedCopy
1792
     * @param array|null  $assoc
1793
     *
1794
     * @return object The managed copy of the entity.
1795
     *
1796
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1797
     *         attribute and the version check against the managed copy fails.
1798
     * @throws ORMInvalidArgumentException If the entity instance is NEW.
1799
     * @throws EntityNotFoundException
1800
     */
1801 40
    private function doMerge($entity, array &$visited, $prevManagedCopy = null, $assoc = null)
1802
    {
1803 40
        $oid = spl_object_hash($entity);
1804
1805 40
        if (isset($visited[$oid])) {
1806 4
            $managedCopy = $visited[$oid];
1807
1808 4
            if ($prevManagedCopy !== null) {
1809 4
                $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy);
0 ignored issues
show
Bug introduced by
It seems like $assoc defined by parameter $assoc on line 1801 can also be of type null; however, Doctrine\ORM\UnitOfWork:...ationWithMergedEntity() does only seem to accept array, maybe add an additional type check?

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

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

An additional type check may prevent trouble.

Loading history...
1810
            }
1811
1812 4
            return $managedCopy;
1813
        }
1814
1815 40
        $class = $this->em->getClassMetadata(get_class($entity));
1816
1817
        // First we assume DETACHED, although it can still be NEW but we can avoid
1818
        // an extra db-roundtrip this way. If it is not MANAGED but has an identity,
1819
        // we need to fetch it from the db anyway in order to merge.
1820
        // MANAGED entities are ignored by the merge operation.
1821 40
        $managedCopy = $entity;
1822
1823 40
        if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) {
1824
            // Try to look the entity up in the identity map.
1825 39
            $id = $class->getIdentifierValues($entity);
1826
1827
            // If there is no ID, it is actually NEW.
1828 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...
1829 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...
1830
1831 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...
1832
            } else {
1833 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...
1834 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...
1835 35
                    : $id;
1836
1837 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 1901 which is incompatible with the return type documented by Doctrine\ORM\UnitOfWork::doMerge of type object.
Loading history...
1838
1839 35
                if ($managedCopy) {
1840
                    // We have the entity in-memory already, just make sure its not removed.
1841 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 1837 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...
1842 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 1837 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...
1843
                    }
1844
                } else {
1845
                    // We need to fetch the managed copy in order to merge.
1846 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...
1847
                }
1848
1849 35
                if ($managedCopy === null) {
1850
                    // If the identifier is ASSIGNED, it is NEW, otherwise an error
1851
                    // since the managed entity was not found.
1852 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...
1853 1
                        throw EntityNotFoundException::fromClassNameAndIdentifier(
1854 1
                            $class->getName(),
1855 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...
1856
                        );
1857
                    }
1858
1859 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...
1860 1
                    $class->setIdentifierValues($managedCopy, $id);
1861
1862 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...
1863
                }
1864
            }
1865
1866 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 1837 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...
1867 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...
1868 4
                $managedCopyVersion = $reflField->getValue($managedCopy);
1869 4
                $entityVersion      = $reflField->getValue($entity);
1870
1871
                // Throw exception if versions don't match.
1872 4
                if ($managedCopyVersion != $entityVersion) {
1873 1
                    throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion);
1874
                }
1875
            }
1876
1877 37
            $visited[$oid] = $managedCopy; // mark visited
1878
1879 37
            if ($this->isLoaded($entity)) {
1880 30
                if ($managedCopy instanceof Proxy && ! $managedCopy->__isInitialized()) {
1881 4
                    $managedCopy->__load();
1882
                }
1883
1884 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 1837 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...
1885
            }
1886
1887 37
            if ($class->isChangeTrackingDeferredExplicit()) {
1888
                $this->scheduleForDirtyCheck($entity);
1889
            }
1890
        }
1891
1892 38
        if ($prevManagedCopy !== null) {
1893 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 1837 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...
Bug introduced by
It seems like $assoc defined by parameter $assoc on line 1801 can also be of type null; however, Doctrine\ORM\UnitOfWork:...ationWithMergedEntity() does only seem to accept array, maybe add an additional type check?

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

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

An additional type check may prevent trouble.

Loading history...
1894
        }
1895
1896
        // Mark the managed copy visited as well
1897 38
        $visited[spl_object_hash($managedCopy)] = $managedCopy;
1898
1899 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 1837 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...
1900
1901 38
        return $managedCopy;
1902
    }
1903
1904
    /**
1905
     * Tests if an entity is loaded - must either be a loaded proxy or not a proxy
1906
     *
1907
     * @param object $entity
1908
     *
1909
     * @return bool
1910
     */
1911 38
    private function isLoaded($entity)
1912
    {
1913 38
        return !($entity instanceof Proxy) || $entity->__isInitialized();
1914
    }
1915
1916
    /**
1917
     * Sets/adds associated managed copies into the previous entity's association field
1918
     *
1919
     * @param object $entity
1920
     * @param array  $association
1921
     * @param object $previousManagedCopy
1922
     * @param object $managedCopy
1923
     *
1924
     * @return void
1925
     */
1926 6
    private function updateAssociationWithMergedEntity($entity, array $association, $previousManagedCopy, $managedCopy)
1927
    {
1928 6
        $assocField = $association['fieldName'];
1929 6
        $prevClass  = $this->em->getClassMetadata(get_class($previousManagedCopy));
1930
1931 6
        if ($association['type'] & ClassMetadata::TO_ONE) {
1932 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...
1933
1934 6
            return;
1935
        }
1936
1937 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...
1938 1
        $value[] = $managedCopy;
1939
1940 1
        if ($association['type'] == ClassMetadata::ONE_TO_MANY) {
1941 1
            $class = $this->em->getClassMetadata(get_class($entity));
1942
1943 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...
1944
        }
1945 1
    }
1946
1947
    /**
1948
     * Detaches an entity from the persistence management. It's persistence will
1949
     * no longer be managed by Doctrine.
1950
     *
1951
     * @param object $entity The entity to detach.
1952
     *
1953
     * @return void
1954
     */
1955 12
    public function detach($entity)
1956
    {
1957 12
        $visited = array();
1958
1959 12
        $this->doDetach($entity, $visited);
1960 12
    }
1961
1962
    /**
1963
     * Executes a detach operation on the given entity.
1964
     *
1965
     * @param object  $entity
1966
     * @param array   $visited
1967
     * @param boolean $noCascade if true, don't cascade detach operation.
1968
     *
1969
     * @return void
1970
     */
1971 15
    private function doDetach($entity, array &$visited, $noCascade = false)
1972
    {
1973 15
        $oid = spl_object_hash($entity);
1974
1975 15
        if (isset($visited[$oid])) {
1976
            return; // Prevent infinite recursion
1977
        }
1978
1979 15
        $visited[$oid] = $entity; // mark visited
1980
1981 15
        switch ($this->getEntityState($entity, self::STATE_DETACHED)) {
1982 15
            case self::STATE_MANAGED:
1983 13
                if ($this->isInIdentityMap($entity)) {
1984 12
                    $this->removeFromIdentityMap($entity);
1985
                }
1986
1987
                unset(
1988 13
                    $this->entityInsertions[$oid],
1989 13
                    $this->entityUpdates[$oid],
1990 13
                    $this->entityDeletions[$oid],
1991 13
                    $this->entityIdentifiers[$oid],
1992 13
                    $this->entityStates[$oid],
1993 13
                    $this->originalEntityData[$oid]
1994
                );
1995 13
                break;
1996 3
            case self::STATE_NEW:
1997 3
            case self::STATE_DETACHED:
1998 3
                return;
1999
        }
2000
2001 13
        if ( ! $noCascade) {
2002 13
            $this->cascadeDetach($entity, $visited);
2003
        }
2004 13
    }
2005
2006
    /**
2007
     * Refreshes the state of the given entity from the database, overwriting
2008
     * any local, unpersisted changes.
2009
     *
2010
     * @param object $entity The entity to refresh.
2011
     *
2012
     * @return void
2013
     *
2014
     * @throws InvalidArgumentException If the entity is not MANAGED.
2015
     */
2016 16
    public function refresh($entity)
2017
    {
2018 16
        $visited = array();
2019
2020 16
        $this->doRefresh($entity, $visited);
2021 16
    }
2022
2023
    /**
2024
     * Executes a refresh operation on an entity.
2025
     *
2026
     * @param object $entity  The entity to refresh.
2027
     * @param array  $visited The already visited entities during cascades.
2028
     *
2029
     * @return void
2030
     *
2031
     * @throws ORMInvalidArgumentException If the entity is not MANAGED.
2032
     */
2033 16
    private function doRefresh($entity, array &$visited)
2034
    {
2035 16
        $oid = spl_object_hash($entity);
2036
2037 16
        if (isset($visited[$oid])) {
2038
            return; // Prevent infinite recursion
2039
        }
2040
2041 16
        $visited[$oid] = $entity; // mark visited
2042
2043 16
        $class = $this->em->getClassMetadata(get_class($entity));
2044
2045 16
        if ($this->getEntityState($entity) !== self::STATE_MANAGED) {
2046
            throw ORMInvalidArgumentException::entityNotManaged($entity);
2047
        }
2048
2049 16
        $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...
2050 16
            array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
2051
            $entity
2052
        );
2053
2054 16
        $this->cascadeRefresh($entity, $visited);
2055 16
    }
2056
2057
    /**
2058
     * Cascades a refresh operation to associated entities.
2059
     *
2060
     * @param object $entity
2061
     * @param array  $visited
2062
     *
2063
     * @return void
2064
     */
2065 16
    private function cascadeRefresh($entity, array &$visited)
2066
    {
2067 16
        $class = $this->em->getClassMetadata(get_class($entity));
2068
2069 16
        $associationMappings = array_filter(
2070 16
            $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...
2071
            function ($assoc) { return $assoc['isCascadeRefresh']; }
2072
        );
2073
2074 16
        foreach ($associationMappings as $assoc) {
2075 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...
2076
2077
            switch (true) {
2078 5
                case ($relatedEntities instanceof PersistentCollection):
2079
                    // Unwrap so that foreach() does not initialize
2080 5
                    $relatedEntities = $relatedEntities->unwrap();
2081
                    // break; is commented intentionally!
2082
2083
                case ($relatedEntities instanceof Collection):
2084
                case (is_array($relatedEntities)):
2085 5
                    foreach ($relatedEntities as $relatedEntity) {
2086
                        $this->doRefresh($relatedEntity, $visited);
2087
                    }
2088 5
                    break;
2089
2090
                case ($relatedEntities !== null):
2091
                    $this->doRefresh($relatedEntities, $visited);
2092
                    break;
2093
2094 5
                default:
2095
                    // Do nothing
2096
            }
2097
        }
2098 16
    }
2099
2100
    /**
2101
     * Cascades a detach operation to associated entities.
2102
     *
2103
     * @param object $entity
2104
     * @param array  $visited
2105
     *
2106
     * @return void
2107
     */
2108 13
    private function cascadeDetach($entity, array &$visited)
2109
    {
2110 13
        $class = $this->em->getClassMetadata(get_class($entity));
2111
2112 13
        $associationMappings = array_filter(
2113 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...
2114
            function ($assoc) { return $assoc['isCascadeDetach']; }
2115
        );
2116
2117 13
        foreach ($associationMappings as $assoc) {
2118 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...
2119
2120
            switch (true) {
2121 3
                case ($relatedEntities instanceof PersistentCollection):
2122
                    // Unwrap so that foreach() does not initialize
2123 2
                    $relatedEntities = $relatedEntities->unwrap();
2124
                    // break; is commented intentionally!
2125
2126
                case ($relatedEntities instanceof Collection):
2127
                case (is_array($relatedEntities)):
2128 3
                    foreach ($relatedEntities as $relatedEntity) {
2129 1
                        $this->doDetach($relatedEntity, $visited);
2130
                    }
2131 3
                    break;
2132
2133
                case ($relatedEntities !== null):
2134
                    $this->doDetach($relatedEntities, $visited);
2135
                    break;
2136
2137 3
                default:
2138
                    // Do nothing
2139
            }
2140
        }
2141 13
    }
2142
2143
    /**
2144
     * Cascades a merge operation to associated entities.
2145
     *
2146
     * @param object $entity
2147
     * @param object $managedCopy
2148
     * @param array  $visited
2149
     *
2150
     * @return void
2151
     */
2152 38
    private function cascadeMerge($entity, $managedCopy, array &$visited)
2153
    {
2154 38
        $class = $this->em->getClassMetadata(get_class($entity));
2155
2156 38
        $associationMappings = array_filter(
2157 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...
2158
            function ($assoc) { return $assoc['isCascadeMerge']; }
2159
        );
2160
2161 38
        foreach ($associationMappings as $assoc) {
2162 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...
2163
2164 15
            if ($relatedEntities instanceof Collection) {
2165 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...
2166 1
                    continue;
2167
                }
2168
2169 8
                if ($relatedEntities instanceof PersistentCollection) {
2170
                    // Unwrap so that foreach() does not initialize
2171 5
                    $relatedEntities = $relatedEntities->unwrap();
2172
                }
2173
2174 8
                foreach ($relatedEntities as $relatedEntity) {
2175 8
                    $this->doMerge($relatedEntity, $visited, $managedCopy, $assoc);
2176
                }
2177 7
            } else if ($relatedEntities !== null) {
2178 14
                $this->doMerge($relatedEntities, $visited, $managedCopy, $assoc);
2179
            }
2180
        }
2181 38
    }
2182
2183
    /**
2184
     * Cascades the save operation to associated entities.
2185
     *
2186
     * @param object $entity
2187
     * @param array  $visited
2188
     *
2189
     * @return void
2190
     */
2191 1019
    private function cascadePersist($entity, array &$visited)
2192
    {
2193 1019
        $class = $this->em->getClassMetadata(get_class($entity));
2194
2195 1019
        $associationMappings = array_filter(
2196 1019
            $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...
2197
            function ($assoc) { return $assoc['isCascadePersist']; }
2198
        );
2199
2200 1019
        foreach ($associationMappings as $assoc) {
2201 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...
2202
2203
            switch (true) {
2204 649
                case ($relatedEntities instanceof PersistentCollection):
2205
                    // Unwrap so that foreach() does not initialize
2206 21
                    $relatedEntities = $relatedEntities->unwrap();
2207
                    // break; is commented intentionally!
2208
2209
                case ($relatedEntities instanceof Collection):
2210 589
                case (is_array($relatedEntities)):
2211 553
                    if (($assoc['type'] & ClassMetadata::TO_MANY) <= 0) {
2212 3
                        throw ORMInvalidArgumentException::invalidAssociation(
2213 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...
2214
                            $assoc,
2215
                            $relatedEntities
2216
                        );
2217
                    }
2218
2219 550
                    foreach ($relatedEntities as $relatedEntity) {
2220 282
                        $this->doPersist($relatedEntity, $visited);
2221
                    }
2222
2223 550
                    break;
2224
2225 579
                case ($relatedEntities !== null):
2226 246
                    if (! $relatedEntities instanceof $assoc['targetEntity']) {
2227 4
                        throw ORMInvalidArgumentException::invalidAssociation(
2228 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...
2229
                            $assoc,
2230
                            $relatedEntities
2231
                        );
2232
                    }
2233
2234 242
                    $this->doPersist($relatedEntities, $visited);
2235 242
                    break;
2236
2237 643
                default:
2238
                    // Do nothing
2239
            }
2240
        }
2241 1012
    }
2242
2243
    /**
2244
     * Cascades the delete operation to associated entities.
2245
     *
2246
     * @param object $entity
2247
     * @param array  $visited
2248
     *
2249
     * @return void
2250
     */
2251 64
    private function cascadeRemove($entity, array &$visited)
2252
    {
2253 64
        $class = $this->em->getClassMetadata(get_class($entity));
2254
2255 64
        $associationMappings = array_filter(
2256 64
            $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...
2257
            function ($assoc) { return $assoc['isCascadeRemove']; }
2258
        );
2259
2260 64
        $entitiesToCascade = array();
2261
2262 64
        foreach ($associationMappings as $assoc) {
2263 25
            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...
2264 6
                $entity->__load();
2265
            }
2266
2267 25
            $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...
2268
2269
            switch (true) {
2270 25
                case ($relatedEntities instanceof Collection):
2271 18
                case (is_array($relatedEntities)):
2272
                    // If its a PersistentCollection initialization is intended! No unwrap!
2273 20
                    foreach ($relatedEntities as $relatedEntity) {
2274 10
                        $entitiesToCascade[] = $relatedEntity;
2275
                    }
2276 20
                    break;
2277
2278 18
                case ($relatedEntities !== null):
2279 7
                    $entitiesToCascade[] = $relatedEntities;
2280 7
                    break;
2281
2282 25
                default:
2283
                    // Do nothing
2284
            }
2285
        }
2286
2287 64
        foreach ($entitiesToCascade as $relatedEntity) {
2288 16
            $this->doRemove($relatedEntity, $visited);
2289
        }
2290 64
    }
2291
2292
    /**
2293
     * Acquire a lock on the given entity.
2294
     *
2295
     * @param object $entity
2296
     * @param int    $lockMode
2297
     * @param int    $lockVersion
2298
     *
2299
     * @return void
2300
     *
2301
     * @throws ORMInvalidArgumentException
2302
     * @throws TransactionRequiredException
2303
     * @throws OptimisticLockException
2304
     */
2305 11
    public function lock($entity, $lockMode, $lockVersion = null)
2306
    {
2307 11
        if ($entity === null) {
2308 1
            throw new \InvalidArgumentException("No entity passed to UnitOfWork#lock().");
2309
        }
2310
2311 10
        if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) {
2312 1
            throw ORMInvalidArgumentException::entityNotManaged($entity);
2313
        }
2314
2315 9
        $class = $this->em->getClassMetadata(get_class($entity));
2316
2317
        switch (true) {
2318 9
            case LockMode::OPTIMISTIC === $lockMode:
2319 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...
2320 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...
2321
                }
2322
2323 4
                if ($lockVersion === null) {
2324
                    return;
2325
                }
2326
2327 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...
2328 1
                    $entity->__load();
2329
                }
2330
2331 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...
2332
2333 4
                if ($entityVersion != $lockVersion) {
2334 2
                    throw OptimisticLockException::lockFailedVersionMismatch($entity, $lockVersion, $entityVersion);
2335
                }
2336
2337 2
                break;
2338
2339 3
            case LockMode::NONE === $lockMode:
2340 3
            case LockMode::PESSIMISTIC_READ === $lockMode:
2341 1
            case LockMode::PESSIMISTIC_WRITE === $lockMode:
2342 3
                if (!$this->em->getConnection()->isTransactionActive()) {
2343 2
                    throw TransactionRequiredException::transactionRequired();
2344
                }
2345
2346 1
                $oid = spl_object_hash($entity);
2347
2348 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...
2349 1
                    array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
2350
                    $lockMode
2351
                );
2352 1
                break;
2353
2354
            default:
2355
                // Do nothing
2356
        }
2357 3
    }
2358
2359
    /**
2360
     * Gets the CommitOrderCalculator used by the UnitOfWork to order commits.
2361
     *
2362
     * @return \Doctrine\ORM\Internal\CommitOrderCalculator
2363
     */
2364 1005
    public function getCommitOrderCalculator()
2365
    {
2366 1005
        return new Internal\CommitOrderCalculator();
2367
    }
2368
2369
    /**
2370
     * Clears the UnitOfWork.
2371
     *
2372
     * @param string|null $entityName if given, only entities of this type will get detached.
2373
     *
2374
     * @return void
2375
     */
2376 1216
    public function clear($entityName = null)
2377
    {
2378 1216
        if ($entityName === null) {
2379 1215
            $this->identityMap =
2380 1215
            $this->entityIdentifiers =
2381 1215
            $this->originalEntityData =
2382 1215
            $this->entityChangeSets =
2383 1215
            $this->entityStates =
2384 1215
            $this->scheduledForSynchronization =
2385 1215
            $this->entityInsertions =
2386 1215
            $this->entityUpdates =
2387 1215
            $this->entityDeletions =
2388 1215
            $this->collectionDeletions =
2389 1215
            $this->collectionUpdates =
2390 1215
            $this->extraUpdates =
2391 1215
            $this->readOnlyObjects =
2392 1215
            $this->visitedCollections =
2393 1215
            $this->orphanRemovals = array();
2394
        } else {
2395 3
            $this->clearIdentityMapForEntityName($entityName);
2396 3
            $this->clearEntityInsertionsForEntityName($entityName);
2397
        }
2398
2399 1216
        if ($this->evm->hasListeners(Events::onClear)) {
2400 7
            $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em, $entityName));
2401
        }
2402 1216
    }
2403
2404
    /**
2405
     * INTERNAL:
2406
     * Schedules an orphaned entity for removal. The remove() operation will be
2407
     * invoked on that entity at the beginning of the next commit of this
2408
     * UnitOfWork.
2409
     *
2410
     * @ignore
2411
     *
2412
     * @param object $entity
2413
     *
2414
     * @return void
2415
     */
2416 17
    public function scheduleOrphanRemoval($entity)
2417
    {
2418 17
        $this->orphanRemovals[spl_object_hash($entity)] = $entity;
2419 17
    }
2420
2421
    /**
2422
     * INTERNAL:
2423
     * Cancels a previously scheduled orphan removal.
2424
     *
2425
     * @ignore
2426
     *
2427
     * @param object $entity
2428
     *
2429
     * @return void
2430
     */
2431 111
    public function cancelOrphanRemoval($entity)
2432
    {
2433 111
        unset($this->orphanRemovals[spl_object_hash($entity)]);
2434 111
    }
2435
2436
    /**
2437
     * INTERNAL:
2438
     * Schedules a complete collection for removal when this UnitOfWork commits.
2439
     *
2440
     * @param PersistentCollection $coll
2441
     *
2442
     * @return void
2443
     */
2444 13
    public function scheduleCollectionDeletion(PersistentCollection $coll)
2445
    {
2446 13
        $coid = spl_object_hash($coll);
2447
2448
        // TODO: if $coll is already scheduled for recreation ... what to do?
2449
        // Just remove $coll from the scheduled recreations?
2450 13
        if (isset($this->collectionUpdates[$coid])) {
2451
            unset($this->collectionUpdates[$coid]);
2452
        }
2453
2454 13
        $this->collectionDeletions[$coid] = $coll;
2455 13
    }
2456
2457
    /**
2458
     * @param PersistentCollection $coll
2459
     *
2460
     * @return bool
2461
     */
2462
    public function isCollectionScheduledForDeletion(PersistentCollection $coll)
2463
    {
2464
        return isset($this->collectionDeletions[spl_object_hash($coll)]);
2465
    }
2466
2467
    /**
2468
     * @param ClassMetadata $class
2469
     *
2470
     * @return \Doctrine\Common\Persistence\ObjectManagerAware|object
2471
     */
2472 667
    private function newInstance($class)
2473
    {
2474 667
        $entity = $class->newInstance();
2475
2476 667
        if ($entity instanceof \Doctrine\Common\Persistence\ObjectManagerAware) {
2477 4
            $entity->injectObjectManager($this->em, $class);
2478
        }
2479
2480 667
        return $entity;
2481
    }
2482
2483
    /**
2484
     * INTERNAL:
2485
     * Creates an entity. Used for reconstitution of persistent entities.
2486
     *
2487
     * Internal note: Highly performance-sensitive method.
2488
     *
2489
     * @ignore
2490
     *
2491
     * @param string $className The name of the entity class.
2492
     * @param array  $data      The data for the entity.
2493
     * @param array  $hints     Any hints to account for during reconstitution/lookup of the entity.
2494
     *
2495
     * @return object The managed entity instance.
2496
     *
2497
     * @todo Rename: getOrCreateEntity
2498
     */
2499 814
    public function createEntity($className, array $data, &$hints = array())
2500
    {
2501 814
        $class = $this->em->getClassMetadata($className);
2502
        //$isReadOnly = isset($hints[Query::HINT_READ_ONLY]);
2503
2504 814
        $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...
2505 814
        $idHash = implode(' ', $id);
2506
2507 814
        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...
2508 341
            $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...
2509 341
            $oid = spl_object_hash($entity);
2510
2511
            if (
2512 341
                isset($hints[Query::HINT_REFRESH])
2513 341
                && isset($hints[Query::HINT_REFRESH_ENTITY])
2514 341
                && ($unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY]) !== $entity
2515 341
                && $unmanagedProxy instanceof Proxy
2516 341
                && $this->isIdentifierEquals($unmanagedProxy, $entity)
2517
            ) {
2518
                // DDC-1238 - we have a managed instance, but it isn't the provided one.
2519
                // Therefore we clear its identifier. Also, we must re-fetch metadata since the
2520
                // refreshed object may be anything
2521
2522 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...
2523 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...
2524
                }
2525
2526 2
                return $unmanagedProxy;
2527
            }
2528
2529 339
            if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
2530 26
                $entity->__setInitialized(true);
2531
2532 26
                $overrideLocalValues = true;
2533
2534 26
                if ($entity instanceof NotifyPropertyChanged) {
2535 26
                    $entity->addPropertyChangedListener($this);
2536
                }
2537
            } else {
2538 317
                $overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
2539
2540
                // If only a specific entity is set to refresh, check that it's the one
2541 317
                if (isset($hints[Query::HINT_REFRESH_ENTITY])) {
2542 68
                    $overrideLocalValues = $hints[Query::HINT_REFRESH_ENTITY] === $entity;
2543
                }
2544
            }
2545
2546 339
            if ($overrideLocalValues) {
2547
                // inject ObjectManager upon refresh.
2548 147
                if ($entity instanceof ObjectManagerAware) {
2549 3
                    $entity->injectObjectManager($this->em, $class);
2550
                }
2551
2552 339
                $this->originalEntityData[$oid] = $data;
2553
            }
2554
        } else {
2555 664
            $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...
2556 664
            $oid    = spl_object_hash($entity);
2557
2558 664
            $this->entityIdentifiers[$oid]  = $id;
2559 664
            $this->entityStates[$oid]       = self::STATE_MANAGED;
2560 664
            $this->originalEntityData[$oid] = $data;
2561
2562 664
            $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...
2563
2564 664
            if ($entity instanceof NotifyPropertyChanged) {
2565 2
                $entity->addPropertyChangedListener($this);
2566
            }
2567
2568 664
            $overrideLocalValues = true;
2569
        }
2570
2571 813
        if ( ! $overrideLocalValues) {
2572 220
            return $entity;
2573
        }
2574
2575 710
        foreach ($data as $field => $value) {
2576 710
            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...
2577 710
                $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...
2578
            }
2579
        }
2580
2581
        // Loading the entity right here, if its in the eager loading map get rid of it there.
2582 710
        unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
2583
2584 710
        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...
2585
            unset($this->eagerLoadingEntities[$class->rootEntityName]);
2586
        }
2587
2588
        // Properly initialize any unfetched associations, if partial objects are not allowed.
2589 710
        if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
2590 33
            return $entity;
2591
        }
2592
2593 677
        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...
2594
            // Check if the association is not among the fetch-joined associations already.
2595 593
            if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) {
2596 263
                continue;
2597
            }
2598
2599 574
            $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
2600
2601
            switch (true) {
2602 574
                case ($assoc['type'] & ClassMetadata::TO_ONE):
2603 493
                    if ( ! $assoc['isOwningSide']) {
2604
2605
                        // use the given entity association
2606 64
                        if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2607
2608 2
                            $this->originalEntityData[$oid][$field] = $data[$field];
2609
2610 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...
2611 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...
2612
2613 2
                            continue 2;
2614
                        }
2615
2616
                        // Inverse side of x-to-one can never be lazy
2617 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...
2618
2619 62
                        continue 2;
2620
                    }
2621
2622
                    // use the entity association
2623 493
                    if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2624 37
                        $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...
2625 37
                        $this->originalEntityData[$oid][$field] = $data[$field];
2626
2627 37
                        continue;
2628
                    }
2629
2630 486
                    $associatedId = array();
2631
2632
                    // TODO: Is this even computed right in all cases of composite keys?
2633 486
                    foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
2634 486
                        $joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
2635
2636 486
                        if ($joinColumnValue !== null) {
2637 288
                            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...
2638 13
                                $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
2639
                            } else {
2640 288
                                $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...
2641
                            }
2642 291
                        } 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...
2643 291
                            && 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...
2644
                        ) {
2645
                            // the missing key is part of target's entity primary key
2646 7
                            $associatedId = array();
2647 486
                            break;
2648
                        }
2649
                    }
2650
2651 486
                    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...
2652
                        // Foreign key is NULL
2653 291
                        $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...
2654 291
                        $this->originalEntityData[$oid][$field] = null;
2655
2656 291
                        continue;
2657
                    }
2658
2659 288
                    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...
2660 285
                        $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...
2661
                    }
2662
2663
                    // Foreign key is set
2664
                    // Check identity map first
2665
                    // FIXME: Can break easily with composite keys if join column values are in
2666
                    //        wrong order. The correct order is the one in ClassMetadata#identifier.
2667 288
                    $relatedIdHash = implode(' ', $associatedId);
2668
2669
                    switch (true) {
2670 288
                        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...
2671 172
                            $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...
2672
2673
                            // If this is an uninitialized proxy, we are deferring eager loads,
2674
                            // this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
2675
                            // then we can append this entity for eager loading!
2676 172
                            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...
2677 172
                                isset($hints[self::HINT_DEFEREAGERLOAD]) &&
2678 172
                                !$targetClass->isIdentifierComposite &&
0 ignored issues
show
Bug introduced by
Accessing isIdentifierComposite on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2679 172
                                $newValue instanceof Proxy &&
2680 172
                                $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...
2681
2682
                                $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...
2683
                            }
2684
2685 172
                            break;
2686
2687 191
                        case ($targetClass->subClasses):
0 ignored issues
show
Bug introduced by
Accessing subClasses on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2688
                            // If it might be a subtype, it can not be lazy. There isn't even
2689
                            // a way to solve this with deferred eager loading, which means putting
2690
                            // an entity with subclasses at a *-to-one location is really bad! (performance-wise)
2691 30
                            $newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, $associatedId);
2692 30
                            break;
2693
2694
                        default:
2695
                            switch (true) {
2696
                                // We are negating the condition here. Other cases will assume it is valid!
2697 162
                                case ($hints['fetchMode'][$class->name][$field] !== ClassMetadata::FETCH_EAGER):
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2698 155
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2699 155
                                    break;
2700
2701
                                // Deferred eager load only works for single identifier classes
2702 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...
2703
                                    // TODO: Is there a faster approach?
2704 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...
2705
2706 7
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2707 7
                                    break;
2708
2709
                                default:
2710
                                    // TODO: This is very imperformant, ignore it?
2711
                                    $newValue = $this->em->find($assoc['targetEntity'], $associatedId);
2712
                                    break;
2713
                            }
2714
2715
                            // PERF: Inlined & optimized code from UnitOfWork#registerManaged()
2716 162
                            $newValueOid = spl_object_hash($newValue);
2717 162
                            $this->entityIdentifiers[$newValueOid] = $associatedId;
2718 162
                            $this->identityMap[$targetClass->rootEntityName][$relatedIdHash] = $newValue;
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2719
2720
                            if (
2721 162
                                $newValue instanceof NotifyPropertyChanged &&
2722 162
                                ( ! $newValue instanceof Proxy || $newValue->__isInitialized())
2723
                            ) {
2724
                                $newValue->addPropertyChangedListener($this);
2725
                            }
2726 162
                            $this->entityStates[$newValueOid] = self::STATE_MANAGED;
2727
                            // make sure that when an proxy is then finally loaded, $this->originalEntityData is set also!
2728 162
                            break;
2729
                    }
2730
2731 288
                    $this->originalEntityData[$oid][$field] = $newValue;
2732 288
                    $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...
2733
2734 288
                    if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
2735 57
                        $inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']];
0 ignored issues
show
Bug introduced by
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2737
                    }
2738
2739 288
                    break;
2740
2741
                default:
2742
                    // Ignore if its a cached collection
2743 491
                    if (isset($hints[Query::HINT_CACHE_ENABLED]) && $class->getFieldValue($entity, $field) instanceof PersistentCollection) {
2744
                        break;
2745
                    }
2746
2747
                    // use the given collection
2748 491
                    if (isset($data[$field]) && $data[$field] instanceof PersistentCollection) {
2749
2750 2
                        $data[$field]->setOwner($entity, $assoc);
2751
2752 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...
2753 2
                        $this->originalEntityData[$oid][$field] = $data[$field];
2754
2755 2
                        break;
2756
                    }
2757
2758
                    // Inject collection
2759 491
                    $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...
2760 491
                    $pColl->setOwner($entity, $assoc);
2761 491
                    $pColl->setInitialized(false);
2762
2763 491
                    $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...
2764 491
                    $reflField->setValue($entity, $pColl);
2765
2766 491
                    if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
2767 4
                        $this->loadCollection($pColl);
2768 4
                        $pColl->takeSnapshot();
2769
                    }
2770
2771 491
                    $this->originalEntityData[$oid][$field] = $pColl;
2772 574
                    break;
2773
            }
2774
        }
2775
2776 677
        if ($overrideLocalValues) {
2777
            // defer invoking of postLoad event to hydration complete step
2778 677
            $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...
2779
        }
2780
2781 677
        return $entity;
2782
    }
2783
2784
    /**
2785
     * @return void
2786
     */
2787 871
    public function triggerEagerLoads()
2788
    {
2789 871
        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...
2790 871
            return;
2791
        }
2792
2793
        // avoid infinite recursion
2794 7
        $eagerLoadingEntities       = $this->eagerLoadingEntities;
2795 7
        $this->eagerLoadingEntities = array();
2796
2797 7
        foreach ($eagerLoadingEntities as $entityName => $ids) {
2798 7
            if ( ! $ids) {
2799
                continue;
2800
            }
2801
2802 7
            $class = $this->em->getClassMetadata($entityName);
2803
2804 7
            $this->getEntityPersister($entityName)->loadAll(
2805 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...
2806
            );
2807
        }
2808 7
    }
2809
2810
    /**
2811
     * Initializes (loads) an uninitialized persistent collection of an entity.
2812
     *
2813
     * @param \Doctrine\ORM\PersistentCollection $collection The collection to initialize.
2814
     *
2815
     * @return void
2816
     *
2817
     * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733.
2818
     */
2819 144
    public function loadCollection(PersistentCollection $collection)
2820
    {
2821 144
        $assoc     = $collection->getMapping();
2822 144
        $persister = $this->getEntityPersister($assoc['targetEntity']);
2823
2824 144
        switch ($assoc['type']) {
2825 144
            case ClassMetadata::ONE_TO_MANY:
2826 76
                $persister->loadOneToManyCollection($assoc, $collection->getOwner(), $collection);
2827 76
                break;
2828
2829 83
            case ClassMetadata::MANY_TO_MANY:
2830 83
                $persister->loadManyToManyCollection($assoc, $collection->getOwner(), $collection);
2831 83
                break;
2832
        }
2833
2834 144
        $collection->setInitialized(true);
2835 144
    }
2836
2837
    /**
2838
     * Gets the identity map of the UnitOfWork.
2839
     *
2840
     * @return array
2841
     */
2842 2
    public function getIdentityMap()
2843
    {
2844 2
        return $this->identityMap;
2845
    }
2846
2847
    /**
2848
     * Gets the original data of an entity. The original data is the data that was
2849
     * present at the time the entity was reconstituted from the database.
2850
     *
2851
     * @param object $entity
2852
     *
2853
     * @return array
2854
     */
2855 114
    public function getOriginalEntityData($entity)
2856
    {
2857 114
        $oid = spl_object_hash($entity);
2858
2859 114
        if (isset($this->originalEntityData[$oid])) {
2860 111
            return $this->originalEntityData[$oid];
2861
        }
2862
2863 4
        return array();
2864
    }
2865
2866
    /**
2867
     * @ignore
2868
     *
2869
     * @param object $entity
2870
     * @param array  $data
2871
     *
2872
     * @return void
2873
     */
2874
    public function setOriginalEntityData($entity, array $data)
2875
    {
2876
        $this->originalEntityData[spl_object_hash($entity)] = $data;
2877
    }
2878
2879
    /**
2880
     * INTERNAL:
2881
     * Sets a property value of the original data array of an entity.
2882
     *
2883
     * @ignore
2884
     *
2885
     * @param string $oid
2886
     * @param string $property
2887
     * @param mixed  $value
2888
     *
2889
     * @return void
2890
     */
2891 315
    public function setOriginalEntityProperty($oid, $property, $value)
2892
    {
2893 315
        $this->originalEntityData[$oid][$property] = $value;
2894 315
    }
2895
2896
    /**
2897
     * Gets the identifier of an entity.
2898
     * The returned value is always an array of identifier values. If the entity
2899
     * has a composite identifier then the identifier values are in the same
2900
     * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames().
2901
     *
2902
     * @param object $entity
2903
     *
2904
     * @return array The identifier values.
2905
     */
2906 841
    public function getEntityIdentifier($entity)
2907
    {
2908 841
        return $this->entityIdentifiers[spl_object_hash($entity)];
2909
    }
2910
2911
    /**
2912
     * Processes an entity instance to extract their identifier values.
2913
     *
2914
     * @param object $entity The entity instance.
2915
     *
2916
     * @return mixed A scalar value.
2917
     *
2918
     * @throws \Doctrine\ORM\ORMInvalidArgumentException
2919
     */
2920 126
    public function getSingleIdentifierValue($entity)
2921
    {
2922 126
        $class = $this->em->getClassMetadata(get_class($entity));
2923
2924 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...
2925
            throw ORMInvalidArgumentException::invalidCompositeIdentifier();
2926
        }
2927
2928 126
        $values = $this->isInIdentityMap($entity)
2929 113
            ? $this->getEntityIdentifier($entity)
2930 126
            : $class->getIdentifierValues($entity);
2931
2932 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...
2933
    }
2934
2935
    /**
2936
     * Tries to find an entity with the given identifier in the identity map of
2937
     * this UnitOfWork.
2938
     *
2939
     * @param mixed  $id            The entity identifier to look for.
2940
     * @param string $rootClassName The name of the root class of the mapped entity hierarchy.
2941
     *
2942
     * @return object|bool Returns the entity with the specified identifier if it exists in
2943
     *                     this UnitOfWork, FALSE otherwise.
2944
     */
2945 520
    public function tryGetById($id, $rootClassName)
2946
    {
2947 520
        $idHash = implode(' ', (array) $id);
2948
2949 520
        if (isset($this->identityMap[$rootClassName][$idHash])) {
2950 78
            return $this->identityMap[$rootClassName][$idHash];
2951
        }
2952
2953 506
        return false;
2954
    }
2955
2956
    /**
2957
     * Schedules an entity for dirty-checking at commit-time.
2958
     *
2959
     * @param object $entity The entity to schedule for dirty-checking.
2960
     *
2961
     * @return void
2962
     *
2963
     * @todo Rename: scheduleForSynchronization
2964
     */
2965 5
    public function scheduleForDirtyCheck($entity)
2966
    {
2967 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...
2968
2969 5
        $this->scheduledForSynchronization[$rootClassName][spl_object_hash($entity)] = $entity;
2970 5
    }
2971
2972
    /**
2973
     * Checks whether the UnitOfWork has any pending insertions.
2974
     *
2975
     * @return boolean TRUE if this UnitOfWork has pending insertions, FALSE otherwise.
2976
     */
2977
    public function hasPendingInsertions()
2978
    {
2979
        return ! empty($this->entityInsertions);
2980
    }
2981
2982
    /**
2983
     * Calculates the size of the UnitOfWork. The size of the UnitOfWork is the
2984
     * number of entities in the identity map.
2985
     *
2986
     * @return integer
2987
     */
2988 1
    public function size()
2989
    {
2990
        $countArray = array_map(function ($item) { return count($item); }, $this->identityMap);
2991
2992 1
        return array_sum($countArray);
2993
    }
2994
2995
    /**
2996
     * Gets the EntityPersister for an Entity.
2997
     *
2998
     * @param string $entityName The name of the Entity.
2999
     *
3000
     * @return \Doctrine\ORM\Persisters\Entity\EntityPersister
3001
     */
3002 1067
    public function getEntityPersister($entityName)
3003
    {
3004 1067
        if (isset($this->persisters[$entityName])) {
3005 848
            return $this->persisters[$entityName];
3006
        }
3007
3008 1067
        $class = $this->em->getClassMetadata($entityName);
3009
3010
        switch (true) {
3011 1067
            case ($class->isInheritanceTypeNone()):
3012 1030
                $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...
3013 1030
                break;
3014
3015 358
            case ($class->isInheritanceTypeSingleTable()):
3016 212
                $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...
3017 212
                break;
3018
3019 337
            case ($class->isInheritanceTypeJoined()):
3020 337
                $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...
3021 337
                break;
3022
3023
            default:
3024
                throw new \RuntimeException('No persister found for entity.');
3025
        }
3026
3027 1067
        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...
3028 118
            $persister = $this->em->getConfiguration()
3029 118
                ->getSecondLevelCacheConfiguration()
3030 118
                ->getCacheFactory()
3031 118
                ->buildCachedEntityPersister($this->em, $persister, $class);
3032
        }
3033
3034 1067
        $this->persisters[$entityName] = $persister;
3035
3036 1067
        return $this->persisters[$entityName];
3037
    }
3038
3039
    /**
3040
     * Gets a collection persister for a collection-valued association.
3041
     *
3042
     * @param array $association
3043
     *
3044
     * @return \Doctrine\ORM\Persisters\Collection\CollectionPersister
3045
     */
3046 568
    public function getCollectionPersister(array $association)
3047
    {
3048 568
        $role = isset($association['cache'])
3049 75
            ? $association['sourceEntity'] . '::' . $association['fieldName']
3050 568
            : $association['type'];
3051
3052 568
        if (isset($this->collectionPersisters[$role])) {
3053 447
            return $this->collectionPersisters[$role];
3054
        }
3055
3056 568
        $persister = ClassMetadata::ONE_TO_MANY === $association['type']
3057 402
            ? new OneToManyPersister($this->em)
3058 568
            : new ManyToManyPersister($this->em);
3059
3060 568
        if ($this->hasCache && isset($association['cache'])) {
3061 74
            $persister = $this->em->getConfiguration()
3062 74
                ->getSecondLevelCacheConfiguration()
3063 74
                ->getCacheFactory()
3064 74
                ->buildCachedCollectionPersister($this->em, $persister, $association);
3065
        }
3066
3067 568
        $this->collectionPersisters[$role] = $persister;
3068
3069 568
        return $this->collectionPersisters[$role];
3070
    }
3071
3072
    /**
3073
     * INTERNAL:
3074
     * Registers an entity as managed.
3075
     *
3076
     * @param object $entity The entity.
3077
     * @param array  $id     The identifier values.
3078
     * @param array  $data   The original entity data.
3079
     *
3080
     * @return void
3081
     */
3082 199
    public function registerManaged($entity, array $id, array $data)
3083
    {
3084 199
        $oid = spl_object_hash($entity);
3085
3086 199
        $this->entityIdentifiers[$oid]  = $id;
3087 199
        $this->entityStates[$oid]       = self::STATE_MANAGED;
3088 199
        $this->originalEntityData[$oid] = $data;
3089
3090 199
        $this->addToIdentityMap($entity);
3091
3092 198
        if ($entity instanceof NotifyPropertyChanged && ( ! $entity instanceof Proxy || $entity->__isInitialized())) {
3093 1
            $entity->addPropertyChangedListener($this);
3094
        }
3095 198
    }
3096
3097
    /**
3098
     * INTERNAL:
3099
     * Clears the property changeset of the entity with the given OID.
3100
     *
3101
     * @param string $oid The entity's OID.
3102
     *
3103
     * @return void
3104
     */
3105
    public function clearEntityChangeSet($oid)
3106
    {
3107
        $this->entityChangeSets[$oid] = array();
3108
    }
3109
3110
    /* PropertyChangedListener implementation */
3111
3112
    /**
3113
     * Notifies this UnitOfWork of a property change in an entity.
3114
     *
3115
     * @param object $entity       The entity that owns the property.
3116
     * @param string $propertyName The name of the property that changed.
3117
     * @param mixed  $oldValue     The old value of the property.
3118
     * @param mixed  $newValue     The new value of the property.
3119
     *
3120
     * @return void
3121
     */
3122 3
    public function propertyChanged($entity, $propertyName, $oldValue, $newValue)
3123
    {
3124 3
        $oid   = spl_object_hash($entity);
3125 3
        $class = $this->em->getClassMetadata(get_class($entity));
3126
3127 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...
3128
3129 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...
3130 1
            return; // ignore non-persistent fields
3131
        }
3132
3133
        // Update changeset and mark entity for synchronization
3134 3
        $this->entityChangeSets[$oid][$propertyName] = array($oldValue, $newValue);
3135
3136 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...
3137 3
            $this->scheduleForDirtyCheck($entity);
3138
        }
3139 3
    }
3140
3141
    /**
3142
     * Gets the currently scheduled entity insertions in this UnitOfWork.
3143
     *
3144
     * @return array
3145
     */
3146 2
    public function getScheduledEntityInsertions()
3147
    {
3148 2
        return $this->entityInsertions;
3149
    }
3150
3151
    /**
3152
     * Gets the currently scheduled entity updates in this UnitOfWork.
3153
     *
3154
     * @return array
3155
     */
3156 2
    public function getScheduledEntityUpdates()
3157
    {
3158 2
        return $this->entityUpdates;
3159
    }
3160
3161
    /**
3162
     * Gets the currently scheduled entity deletions in this UnitOfWork.
3163
     *
3164
     * @return array
3165
     */
3166 1
    public function getScheduledEntityDeletions()
3167
    {
3168 1
        return $this->entityDeletions;
3169
    }
3170
3171
    /**
3172
     * Gets the currently scheduled complete collection deletions
3173
     *
3174
     * @return array
3175
     */
3176 1
    public function getScheduledCollectionDeletions()
3177
    {
3178 1
        return $this->collectionDeletions;
3179
    }
3180
3181
    /**
3182
     * Gets the currently scheduled collection inserts, updates and deletes.
3183
     *
3184
     * @return array
3185
     */
3186
    public function getScheduledCollectionUpdates()
3187
    {
3188
        return $this->collectionUpdates;
3189
    }
3190
3191
    /**
3192
     * Helper method to initialize a lazy loading proxy or persistent collection.
3193
     *
3194
     * @param object $obj
3195
     *
3196
     * @return void
3197
     */
3198 2
    public function initializeObject($obj)
3199
    {
3200 2
        if ($obj instanceof Proxy) {
3201 1
            $obj->__load();
3202
3203 1
            return;
3204
        }
3205
3206 1
        if ($obj instanceof PersistentCollection) {
3207 1
            $obj->initialize();
3208
        }
3209 1
    }
3210
3211
    /**
3212
     * Helper method to show an object as string.
3213
     *
3214
     * @param object $obj
3215
     *
3216
     * @return string
3217
     */
3218 1
    private static function objToStr($obj)
3219
    {
3220 1
        return method_exists($obj, '__toString') ? (string) $obj : get_class($obj).'@'.spl_object_hash($obj);
3221
    }
3222
3223
    /**
3224
     * Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit().
3225
     *
3226
     * This operation cannot be undone as some parts of the UnitOfWork now keep gathering information
3227
     * on this object that might be necessary to perform a correct update.
3228
     *
3229
     * @param object $object
3230
     *
3231
     * @return void
3232
     *
3233
     * @throws ORMInvalidArgumentException
3234
     */
3235 6
    public function markReadOnly($object)
3236
    {
3237 6
        if ( ! is_object($object) || ! $this->isInIdentityMap($object)) {
3238 1
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3239
        }
3240
3241 5
        $this->readOnlyObjects[spl_object_hash($object)] = true;
3242 5
    }
3243
3244
    /**
3245
     * Is this entity read only?
3246
     *
3247
     * @param object $object
3248
     *
3249
     * @return bool
3250
     *
3251
     * @throws ORMInvalidArgumentException
3252
     */
3253 3
    public function isReadOnly($object)
3254
    {
3255 3
        if ( ! is_object($object)) {
3256
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3257
        }
3258
3259 3
        return isset($this->readOnlyObjects[spl_object_hash($object)]);
3260
    }
3261
3262
    /**
3263
     * Perform whatever processing is encapsulated here after completion of the transaction.
3264
     */
3265 1000
    private function afterTransactionComplete()
3266
    {
3267
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3268 88
            $persister->afterTransactionComplete();
3269 1000
        });
3270 1000
    }
3271
3272
    /**
3273
     * Perform whatever processing is encapsulated here after completion of the rolled-back.
3274
     */
3275
    private function afterTransactionRolledBack()
3276
    {
3277 11
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3278 3
            $persister->afterTransactionRolledBack();
3279 11
        });
3280 11
    }
3281
3282
    /**
3283
     * Performs an action after the transaction.
3284
     *
3285
     * @param callable $callback
3286
     */
3287 1005
    private function performCallbackOnCachedPersister(callable $callback)
3288
    {
3289 1005
        if ( ! $this->hasCache) {
3290 917
            return;
3291
        }
3292
3293 88
        foreach (array_merge($this->persisters, $this->collectionPersisters) as $persister) {
3294 88
            if ($persister instanceof CachedPersister) {
3295 88
                $callback($persister);
3296
            }
3297
        }
3298 88
    }
3299
3300 1009
    private function dispatchOnFlushEvent()
3301
    {
3302 1009
        if ($this->evm->hasListeners(Events::onFlush)) {
3303 4
            $this->evm->dispatchEvent(Events::onFlush, new OnFlushEventArgs($this->em));
3304
        }
3305 1009
    }
3306
3307 1004
    private function dispatchPostFlushEvent()
3308
    {
3309 1004
        if ($this->evm->hasListeners(Events::postFlush)) {
3310 5
            $this->evm->dispatchEvent(Events::postFlush, new PostFlushEventArgs($this->em));
3311
        }
3312 1003
    }
3313
3314 999
    private function dispatchAfterFlush()
3315
    {
3316 999
        if($this->evm->hasListeners(Events::afterFlush)) {
3317
            $this->evm->dispatchEvent(Events::afterFlush);
3318
        }
3319 999
    }
3320
3321
    /**
3322
     * Verifies if two given entities actually are the same based on identifier comparison
3323
     *
3324
     * @param object $entity1
3325
     * @param object $entity2
3326
     *
3327
     * @return bool
3328
     */
3329 13
    private function isIdentifierEquals($entity1, $entity2)
3330
    {
3331 13
        if ($entity1 === $entity2) {
3332
            return true;
3333
        }
3334
3335 13
        $class = $this->em->getClassMetadata(get_class($entity1));
3336
3337 13
        if ($class !== $this->em->getClassMetadata(get_class($entity2))) {
3338 10
            return false;
3339
        }
3340
3341 3
        $oid1 = spl_object_hash($entity1);
3342 3
        $oid2 = spl_object_hash($entity2);
3343
3344 3
        $id1 = isset($this->entityIdentifiers[$oid1])
3345 3
            ? $this->entityIdentifiers[$oid1]
3346 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...
3347 3
        $id2 = isset($this->entityIdentifiers[$oid2])
3348 3
            ? $this->entityIdentifiers[$oid2]
3349 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...
3350
3351 3
        return $id1 === $id2 || implode(' ', $id1) === implode(' ', $id2);
3352
    }
3353
3354
    /**
3355
     * @param object $entity
3356
     * @param object $managedCopy
3357
     *
3358
     * @throws ORMException
3359
     * @throws OptimisticLockException
3360
     * @throws TransactionRequiredException
3361
     */
3362 30
    private function mergeEntityStateIntoManagedCopy($entity, $managedCopy)
3363
    {
3364 30
        $class = $this->em->getClassMetadata(get_class($entity));
3365
3366 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...
3367 30
            $name = $prop->name;
3368
3369 30
            $prop->setAccessible(true);
3370
3371 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...
3372 30
                if ( ! $class->isIdentifier($name)) {
3373 30
                    $prop->setValue($managedCopy, $prop->getValue($entity));
3374
                }
3375
            } else {
3376 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...
3377
3378 28
                if ($assoc2['type'] & ClassMetadata::TO_ONE) {
3379 24
                    $other = $prop->getValue($entity);
3380 24
                    if ($other === null) {
3381 11
                        $prop->setValue($managedCopy, null);
3382
                    } else {
3383 16
                        if ($other instanceof Proxy && !$other->__isInitialized()) {
3384
                            // do not merge fields marked lazy that have not been fetched.
3385 4
                            continue;
3386
                        }
3387
3388 12
                        if ( ! $assoc2['isCascadeMerge']) {
3389 6
                            if ($this->getEntityState($other) === self::STATE_DETACHED) {
3390 3
                                $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']);
3391 3
                                $relatedId   = $targetClass->getIdentifierValues($other);
3392
3393 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...
3394 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...
3395
                                } else {
3396 1
                                    $other = $this->em->getProxyFactory()->getProxy(
3397 1
                                        $assoc2['targetEntity'],
3398
                                        $relatedId
3399
                                    );
3400 1
                                    $this->registerManaged($other, $relatedId, array());
3401
                                }
3402
                            }
3403
3404 20
                            $prop->setValue($managedCopy, $other);
3405
                        }
3406
                    }
3407
                } else {
3408 16
                    $mergeCol = $prop->getValue($entity);
3409
3410 16
                    if ($mergeCol instanceof PersistentCollection && ! $mergeCol->isInitialized()) {
3411
                        // do not merge fields marked lazy that have not been fetched.
3412
                        // keep the lazy persistent collection of the managed copy.
3413 5
                        continue;
3414
                    }
3415
3416 13
                    $managedCol = $prop->getValue($managedCopy);
3417
3418 13
                    if ( ! $managedCol) {
3419 3
                        $managedCol = new PersistentCollection(
3420 3
                            $this->em,
3421 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...
3422 3
                            new ArrayCollection
3423
                        );
3424 3
                        $managedCol->setOwner($managedCopy, $assoc2);
3425 3
                        $prop->setValue($managedCopy, $managedCol);
3426
3427 3
                        $this->originalEntityData[spl_object_hash($entity)][$name] = $managedCol;
3428
                    }
3429
3430 13
                    if ($assoc2['isCascadeMerge']) {
3431 8
                        $managedCol->initialize();
3432
3433
                        // clear and set dirty a managed collection if its not also the same collection to merge from.
3434 8
                        if ( ! $managedCol->isEmpty() && $managedCol !== $mergeCol) {
3435 1
                            $managedCol->unwrap()->clear();
3436 1
                            $managedCol->setDirty(true);
3437
3438 1
                            if ($assoc2['isOwningSide']
3439 1
                                && $assoc2['type'] == ClassMetadata::MANY_TO_MANY
3440 1
                                && $class->isChangeTrackingNotify()
3441
                            ) {
3442
                                $this->scheduleForDirtyCheck($managedCopy);
3443
                            }
3444
                        }
3445
                    }
3446
                }
3447
            }
3448
3449 30
            if ($class->isChangeTrackingNotify()) {
3450
                // Just treat all properties as changed, there is no other choice.
3451 30
                $this->propertyChanged($managedCopy, $name, null, $prop->getValue($managedCopy));
3452
            }
3453
        }
3454 30
    }
3455
3456
    /**
3457
     * This method called by hydrators, and indicates that hydrator totally completed current hydration cycle.
3458
     * Unit of work able to fire deferred events, related to loading events here.
3459
     *
3460
     * @internal should be called internally from object hydrators
3461
     */
3462 882
    public function hydrationComplete()
3463
    {
3464 882
        $this->hydrationCompleteHandler->hydrationComplete();
3465 882
    }
3466
3467
    /**
3468
     * @param string $entityName
3469
     */
3470 3
    private function clearIdentityMapForEntityName($entityName)
3471
    {
3472 3
        if (! isset($this->identityMap[$entityName])) {
3473
            return;
3474
        }
3475
3476 3
        $visited = [];
3477
3478 3
        foreach ($this->identityMap[$entityName] as $entity) {
3479 3
            $this->doDetach($entity, $visited, false);
3480
        }
3481 3
    }
3482
3483
    /**
3484
     * @param string $entityName
3485
     */
3486 3
    private function clearEntityInsertionsForEntityName($entityName)
3487
    {
3488 3
        foreach ($this->entityInsertions as $hash => $entity) {
3489
            // note: performance optimization - `instanceof` is much faster than a function call
3490 1
            if ($entity instanceof $entityName && get_class($entity) === $entityName) {
3491 1
                unset($this->entityInsertions[$hash]);
3492
            }
3493
        }
3494 3
    }
3495
}
3496