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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1132 615
                continue;
1133
            }
1134
1135 994
            $calc->addNode($class->name, $class);
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1136
1137 994
            $newNodes[] = $class;
1138
        }
1139
1140
        // Calculate dependencies for new nodes
1141 994
        while ($class = array_pop($newNodes)) {
1142 994
            foreach ($class->associationMappings as $assoc) {
1143 875
                if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) {
1144 836
                    continue;
1145
                }
1146
1147 828
                $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
1148
1149 828
                if ( ! $calc->hasNode($targetClass->name)) {
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1151
1152 641
                    $newNodes[] = $targetClass;
1153
                }
1154
1155 828
                $joinColumns = reset($assoc['joinColumns']);
1156 828
                $isNullable  = isset($joinColumns['nullable']) ? $joinColumns['nullable'] : false;
1157
1158 828
                $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...
1159
1160
                // If the target class has mapped subclasses, these share the same dependency.
1161 828
                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...
1162 821
                    continue;
1163
                }
1164
1165 216
                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...
1166 216
                    $targetSubClass = $this->em->getClassMetadata($subClassName);
1167
1168 216
                    if ( ! $calc->hasNode($subClassName)) {
1169 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...
1170
1171 189
                        $newNodes[] = $targetSubClass;
1172
                    }
1173
1174 216
                    $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...
1175
                }
1176
            }
1177
        }
1178
1179 994
        return $calc->sort();
1180
    }
1181
1182
    /**
1183
     * Schedules an entity for insertion into the database.
1184
     * If the entity already has an identifier, it will be added to the identity map.
1185
     *
1186
     * @param object $entity The entity to schedule for insertion.
1187
     *
1188
     * @return void
1189
     *
1190
     * @throws ORMInvalidArgumentException
1191
     * @throws \InvalidArgumentException
1192
     */
1193 1010
    public function scheduleForInsert($entity)
1194
    {
1195 1010
        $oid = spl_object_hash($entity);
1196
1197 1010
        if (isset($this->entityUpdates[$oid])) {
1198
            throw new InvalidArgumentException("Dirty entity can not be scheduled for insertion.");
1199
        }
1200
1201 1010
        if (isset($this->entityDeletions[$oid])) {
1202 1
            throw ORMInvalidArgumentException::scheduleInsertForRemovedEntity($entity);
1203
        }
1204 1010
        if (isset($this->originalEntityData[$oid]) && ! isset($this->entityInsertions[$oid])) {
1205 1
            throw ORMInvalidArgumentException::scheduleInsertForManagedEntity($entity);
1206
        }
1207
1208 1010
        if (isset($this->entityInsertions[$oid])) {
1209 1
            throw ORMInvalidArgumentException::scheduleInsertTwice($entity);
1210
        }
1211
1212 1010
        $this->entityInsertions[$oid] = $entity;
1213
1214 1010
        if (isset($this->entityIdentifiers[$oid])) {
1215 257
            $this->addToIdentityMap($entity);
1216
        }
1217
1218 1010
        if ($entity instanceof NotifyPropertyChanged) {
1219 5
            $entity->addPropertyChangedListener($this);
1220
        }
1221 1010
    }
1222
1223
    /**
1224
     * Checks whether an entity is scheduled for insertion.
1225
     *
1226
     * @param object $entity
1227
     *
1228
     * @return boolean
1229
     */
1230 625
    public function isScheduledForInsert($entity)
1231
    {
1232 625
        return isset($this->entityInsertions[spl_object_hash($entity)]);
1233
    }
1234
1235
    /**
1236
     * Schedules an entity for being updated.
1237
     *
1238
     * @param object $entity The entity to schedule for being updated.
1239
     *
1240
     * @return void
1241
     *
1242
     * @throws ORMInvalidArgumentException
1243
     */
1244 1
    public function scheduleForUpdate($entity)
1245
    {
1246 1
        $oid = spl_object_hash($entity);
1247
1248 1
        if ( ! isset($this->entityIdentifiers[$oid])) {
1249
            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "scheduling for update");
1250
        }
1251
1252 1
        if (isset($this->entityDeletions[$oid])) {
1253
            throw ORMInvalidArgumentException::entityIsRemoved($entity, "schedule for update");
1254
        }
1255
1256 1
        if ( ! isset($this->entityUpdates[$oid]) && ! isset($this->entityInsertions[$oid])) {
1257 1
            $this->entityUpdates[$oid] = $entity;
1258
        }
1259 1
    }
1260
1261
    /**
1262
     * INTERNAL:
1263
     * Schedules an extra update that will be executed immediately after the
1264
     * regular entity updates within the currently running commit cycle.
1265
     *
1266
     * Extra updates for entities are stored as (entity, changeset) tuples.
1267
     *
1268
     * @ignore
1269
     *
1270
     * @param object $entity    The entity for which to schedule an extra update.
1271
     * @param array  $changeset The changeset of the entity (what to update).
1272
     *
1273
     * @return void
1274
     */
1275 39
    public function scheduleExtraUpdate($entity, array $changeset)
1276
    {
1277 39
        $oid         = spl_object_hash($entity);
1278 39
        $extraUpdate = array($entity, $changeset);
1279
1280 39
        if (isset($this->extraUpdates[$oid])) {
1281 1
            list($ignored, $changeset2) = $this->extraUpdates[$oid];
0 ignored issues
show
Unused Code introduced by
The assignment to $ignored is unused. Consider omitting it like so list($first,,$third).

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

Consider the following code example.

<?php

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

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

print $a . " - " . $c;

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

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1282
1283 1
            $extraUpdate = array($entity, $changeset + $changeset2);
1284
        }
1285
1286 39
        $this->extraUpdates[$oid] = $extraUpdate;
1287 39
    }
1288
1289
    /**
1290
     * Checks whether an entity is registered as dirty in the unit of work.
1291
     * Note: Is not very useful currently as dirty entities are only registered
1292
     * at commit time.
1293
     *
1294
     * @param object $entity
1295
     *
1296
     * @return boolean
1297
     */
1298
    public function isScheduledForUpdate($entity)
1299
    {
1300
        return isset($this->entityUpdates[spl_object_hash($entity)]);
1301
    }
1302
1303
    /**
1304
     * Checks whether an entity is registered to be checked in the unit of work.
1305
     *
1306
     * @param object $entity
1307
     *
1308
     * @return boolean
1309
     */
1310 1
    public function isScheduledForDirtyCheck($entity)
1311
    {
1312 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...
1313
1314 1
        return isset($this->scheduledForSynchronization[$rootEntityName][spl_object_hash($entity)]);
1315
    }
1316
1317
    /**
1318
     * INTERNAL:
1319
     * Schedules an entity for deletion.
1320
     *
1321
     * @param object $entity
1322
     *
1323
     * @return void
1324
     */
1325 65
    public function scheduleForDelete($entity)
1326
    {
1327 65
        $oid = spl_object_hash($entity);
1328
1329 65
        if (isset($this->entityInsertions[$oid])) {
1330 1
            if ($this->isInIdentityMap($entity)) {
1331
                $this->removeFromIdentityMap($entity);
1332
            }
1333
1334 1
            unset($this->entityInsertions[$oid], $this->entityStates[$oid]);
1335
1336 1
            return; // entity has not been persisted yet, so nothing more to do.
1337
        }
1338
1339 65
        if ( ! $this->isInIdentityMap($entity)) {
1340 1
            return;
1341
        }
1342
1343 64
        $this->removeFromIdentityMap($entity);
1344
1345 64
        if (isset($this->entityUpdates[$oid])) {
1346 2
            unset($this->entityUpdates[$oid]);
1347
        }
1348
1349 64
        if ( ! isset($this->entityDeletions[$oid])) {
1350 64
            $this->entityDeletions[$oid] = $entity;
1351 64
            $this->entityStates[$oid]    = self::STATE_REMOVED;
1352
        }
1353 64
    }
1354
1355
    /**
1356
     * Checks whether an entity is registered as removed/deleted with the unit
1357
     * of work.
1358
     *
1359
     * @param object $entity
1360
     *
1361
     * @return boolean
1362
     */
1363 17
    public function isScheduledForDelete($entity)
1364
    {
1365 17
        return isset($this->entityDeletions[spl_object_hash($entity)]);
1366
    }
1367
1368
    /**
1369
     * Checks whether an entity is scheduled for insertion, update or deletion.
1370
     *
1371
     * @param object $entity
1372
     *
1373
     * @return boolean
1374
     */
1375
    public function isEntityScheduled($entity)
1376
    {
1377
        $oid = spl_object_hash($entity);
1378
1379
        return isset($this->entityInsertions[$oid])
1380
            || isset($this->entityUpdates[$oid])
1381
            || isset($this->entityDeletions[$oid]);
1382
    }
1383
1384
    /**
1385
     * INTERNAL:
1386
     * Registers an entity in the identity map.
1387
     * Note that entities in a hierarchy are registered with the class name of
1388
     * the root entity.
1389
     *
1390
     * @ignore
1391
     *
1392
     * @param object $entity The entity to register.
1393
     *
1394
     * @return boolean TRUE if the registration was successful, FALSE if the identity of
1395
     *                 the entity in question is already managed.
1396
     *
1397
     * @throws ORMInvalidArgumentException
1398
     */
1399 1066
    public function addToIdentityMap($entity)
1400
    {
1401 1066
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1402 1066
        $idHash        = implode(' ', $this->entityIdentifiers[spl_object_hash($entity)]);
1403
1404 1066
        if ($idHash === '') {
1405 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...
1406
        }
1407
1408 1065
        $className = $classMetadata->rootEntityName;
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1409
1410 1065
        if (isset($this->identityMap[$className][$idHash])) {
1411 77
            return false;
1412
        }
1413
1414 1065
        $this->identityMap[$className][$idHash] = $entity;
1415
1416 1065
        return true;
1417
    }
1418
1419
    /**
1420
     * Gets the state of an entity with regard to the current unit of work.
1421
     *
1422
     * @param object   $entity
1423
     * @param int|null $assume The state to assume if the state is not yet known (not MANAGED or REMOVED).
1424
     *                         This parameter can be set to improve performance of entity state detection
1425
     *                         by potentially avoiding a database lookup if the distinction between NEW and DETACHED
1426
     *                         is either known or does not matter for the caller of the method.
1427
     *
1428
     * @return int The entity state.
1429
     */
1430 1023
    public function getEntityState($entity, $assume = null)
1431
    {
1432 1023
        $oid = spl_object_hash($entity);
1433
1434 1023
        if (isset($this->entityStates[$oid])) {
1435 770
            return $this->entityStates[$oid];
1436
        }
1437
1438 1017
        if ($assume !== null) {
1439 1013
            return $assume;
1440
        }
1441
1442
        // State can only be NEW or DETACHED, because MANAGED/REMOVED states are known.
1443
        // Note that you can not remember the NEW or DETACHED state in _entityStates since
1444
        // the UoW does not hold references to such objects and the object hash can be reused.
1445
        // More generally because the state may "change" between NEW/DETACHED without the UoW being aware of it.
1446 13
        $class = $this->em->getClassMetadata(get_class($entity));
1447 13
        $id    = $class->getIdentifierValues($entity);
1448
1449 13
        if ( ! $id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1454 1
            $id = $this->identifierFlattener->flattenIdentifier($class, $id);
0 ignored issues
show
Compatibility introduced by
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ORM\Mapping\ClassMetadata>. It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.

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

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

Loading history...
1455
        }
1456
1457
        switch (true) {
1458 10
            case ($class->isIdentifierNatural()):
0 ignored issues
show
Bug introduced by
The method isIdentifierNatural() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. Did you maybe mean isIdentifier()?

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

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

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

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1473
                    return self::STATE_DETACHED;
1474
                }
1475
1476 4
                return self::STATE_NEW;
1477
1478 5
            case ( ! $class->idGenerator->isPostInsertGenerator()):
0 ignored issues
show
Bug introduced by
Accessing idGenerator on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1479
                // if we have a pre insert generator we can't be sure that having an id
1480
                // really means that the entity exists. We have to verify this through
1481
                // the last resort: a db lookup
1482
1483
                // Last try before db lookup: check the identity map.
1484
                if ($this->tryGetById($id, $class->rootEntityName)) {
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1490
                    return self::STATE_DETACHED;
1491
                }
1492
1493
                return self::STATE_NEW;
1494
1495
            default:
1496 5
                return self::STATE_DETACHED;
1497
        }
1498
    }
1499
1500
    /**
1501
     * INTERNAL:
1502
     * Removes an entity from the identity map. This effectively detaches the
1503
     * entity from the persistence management of Doctrine.
1504
     *
1505
     * @ignore
1506
     *
1507
     * @param object $entity
1508
     *
1509
     * @return boolean
1510
     *
1511
     * @throws ORMInvalidArgumentException
1512
     */
1513 73
    public function removeFromIdentityMap($entity)
1514
    {
1515 73
        $oid           = spl_object_hash($entity);
1516 73
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1517 73
        $idHash        = implode(' ', $this->entityIdentifiers[$oid]);
1518
1519 73
        if ($idHash === '') {
1520
            throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "remove from identity map");
1521
        }
1522
1523 73
        $className = $classMetadata->rootEntityName;
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
1524
1525 73
        if (isset($this->identityMap[$className][$idHash])) {
1526 73
            unset($this->identityMap[$className][$idHash]);
1527 73
            unset($this->readOnlyObjects[$oid]);
1528
1529
            //$this->entityStates[$oid] = self::STATE_DETACHED;
1530
1531 73
            return true;
1532
        }
1533
1534
        return false;
1535
    }
1536
1537
    /**
1538
     * INTERNAL:
1539
     * Gets an entity in the identity map by its identifier hash.
1540
     *
1541
     * @ignore
1542
     *
1543
     * @param string $idHash
1544
     * @param string $rootClassName
1545
     *
1546
     * @return object
1547
     */
1548
    public function getByIdHash($idHash, $rootClassName)
1549
    {
1550
        return $this->identityMap[$rootClassName][$idHash];
1551
    }
1552
1553
    /**
1554
     * INTERNAL:
1555
     * Tries to get an entity by its identifier hash. If no entity is found for
1556
     * the given hash, FALSE is returned.
1557
     *
1558
     * @ignore
1559
     *
1560
     * @param mixed  $idHash        (must be possible to cast it to string)
1561
     * @param string $rootClassName
1562
     *
1563
     * @return object|bool The found entity or FALSE.
1564
     */
1565 34
    public function tryGetByIdHash($idHash, $rootClassName)
1566
    {
1567 34
        $stringIdHash = (string) $idHash;
1568
1569 34
        if (isset($this->identityMap[$rootClassName][$stringIdHash])) {
1570 34
            return $this->identityMap[$rootClassName][$stringIdHash];
1571
        }
1572
1573 2
        return false;
1574
    }
1575
1576
    /**
1577
     * Checks whether an entity is registered in the identity map of this UnitOfWork.
1578
     *
1579
     * @param object $entity
1580
     *
1581
     * @return boolean
1582
     */
1583 209
    public function isInIdentityMap($entity)
1584
    {
1585 209
        $oid = spl_object_hash($entity);
1586
1587 209
        if ( ! isset($this->entityIdentifiers[$oid])) {
1588 30
            return false;
1589
        }
1590
1591 194
        $classMetadata = $this->em->getClassMetadata(get_class($entity));
1592 194
        $idHash        = implode(' ', $this->entityIdentifiers[$oid]);
1593
1594 194
        if ($idHash === '') {
1595
            return false;
1596
        }
1597
1598 194
        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...
1599
    }
1600
1601
    /**
1602
     * INTERNAL:
1603
     * Checks whether an identifier hash exists in the identity map.
1604
     *
1605
     * @ignore
1606
     *
1607
     * @param string $idHash
1608
     * @param string $rootClassName
1609
     *
1610
     * @return boolean
1611
     */
1612
    public function containsIdHash($idHash, $rootClassName)
1613
    {
1614
        return isset($this->identityMap[$rootClassName][$idHash]);
1615
    }
1616
1617
    /**
1618
     * Persists an entity as part of the current unit of work.
1619
     *
1620
     * @param object $entity The entity to persist.
1621
     *
1622
     * @return void
1623
     */
1624 1007
    public function persist($entity)
1625
    {
1626 1007
        $visited = array();
1627
1628 1007
        $this->doPersist($entity, $visited);
1629 999
    }
1630
1631
    /**
1632
     * Persists an entity as part of the current unit of work.
1633
     *
1634
     * This method is internally called during persist() cascades as it tracks
1635
     * the already visited entities to prevent infinite recursions.
1636
     *
1637
     * @param object $entity  The entity to persist.
1638
     * @param array  $visited The already visited entities.
1639
     *
1640
     * @return void
1641
     *
1642
     * @throws ORMInvalidArgumentException
1643
     * @throws UnexpectedValueException
1644
     */
1645 1007
    private function doPersist($entity, array &$visited)
1646
    {
1647 1007
        $oid = spl_object_hash($entity);
1648
1649 1007
        if (isset($visited[$oid])) {
1650 109
            return; // Prevent infinite recursion
1651
        }
1652
1653 1007
        $visited[$oid] = $entity; // Mark visited
1654
1655 1007
        $class = $this->em->getClassMetadata(get_class($entity));
1656
1657
        // We assume NEW, so DETACHED entities result in an exception on flush (constraint violation).
1658
        // If we would detect DETACHED here we would throw an exception anyway with the same
1659
        // consequences (not recoverable/programming error), so just assuming NEW here
1660
        // lets us avoid some database lookups for entities with natural identifiers.
1661 1006
        $entityState = $this->getEntityState($entity, self::STATE_NEW);
1662
1663
        switch ($entityState) {
1664 1006
            case self::STATE_MANAGED:
1665
                // Nothing to do, except if policy is "deferred explicit"
1666 233
                if ($class->isChangeTrackingDeferredExplicit()) {
1667 2
                    $this->scheduleForDirtyCheck($entity);
1668
                }
1669 233
                break;
1670
1671 1006
            case self::STATE_NEW:
1672 1005
                $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...
1673 1005
                break;
1674
1675 1
            case self::STATE_REMOVED:
1676
                // Entity becomes managed again
1677 1
                unset($this->entityDeletions[$oid]);
1678 1
                $this->addToIdentityMap($entity);
1679
1680 1
                $this->entityStates[$oid] = self::STATE_MANAGED;
1681 1
                break;
1682
1683
            case self::STATE_DETACHED:
1684
                // Can actually not happen right now since we assume STATE_NEW.
1685
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "persisted");
1686
1687
            default:
1688
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1689
        }
1690
1691 1006
        $this->cascadePersist($entity, $visited);
1692 999
    }
1693
1694
    /**
1695
     * Deletes an entity as part of the current unit of work.
1696
     *
1697
     * @param object $entity The entity to remove.
1698
     *
1699
     * @return void
1700
     */
1701 64
    public function remove($entity)
1702
    {
1703 64
        $visited = array();
1704
1705 64
        $this->doRemove($entity, $visited);
1706 64
    }
1707
1708
    /**
1709
     * Deletes an entity as part of the current unit of work.
1710
     *
1711
     * This method is internally called during delete() cascades as it tracks
1712
     * the already visited entities to prevent infinite recursions.
1713
     *
1714
     * @param object $entity  The entity to delete.
1715
     * @param array  $visited The map of the already visited entities.
1716
     *
1717
     * @return void
1718
     *
1719
     * @throws ORMInvalidArgumentException If the instance is a detached entity.
1720
     * @throws UnexpectedValueException
1721
     */
1722 64
    private function doRemove($entity, array &$visited)
1723
    {
1724 64
        $oid = spl_object_hash($entity);
1725
1726 64
        if (isset($visited[$oid])) {
1727 1
            return; // Prevent infinite recursion
1728
        }
1729
1730 64
        $visited[$oid] = $entity; // mark visited
1731
1732
        // Cascade first, because scheduleForDelete() removes the entity from the identity map, which
1733
        // can cause problems when a lazy proxy has to be initialized for the cascade operation.
1734 64
        $this->cascadeRemove($entity, $visited);
1735
1736 64
        $class       = $this->em->getClassMetadata(get_class($entity));
1737 64
        $entityState = $this->getEntityState($entity);
1738
1739
        switch ($entityState) {
1740 64
            case self::STATE_NEW:
1741 64
            case self::STATE_REMOVED:
1742
                // nothing to do
1743 2
                break;
1744
1745 64
            case self::STATE_MANAGED:
1746 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...
1747
1748 64
                if ($invoke !== ListenersInvoker::INVOKE_NONE) {
1749 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...
1750
                }
1751
1752 64
                $this->scheduleForDelete($entity);
1753 64
                break;
1754
1755
            case self::STATE_DETACHED:
1756
                throw ORMInvalidArgumentException::detachedEntityCannot($entity, "removed");
1757
            default:
1758
                throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity));
1759
        }
1760
1761 64
    }
1762
1763
    /**
1764
     * Merges the state of the given detached entity into this UnitOfWork.
1765
     *
1766
     * @param object $entity
1767
     *
1768
     * @return object The managed copy of the entity.
1769
     *
1770
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1771
     *         attribute and the version check against the managed copy fails.
1772
     *
1773
     * @todo Require active transaction!? OptimisticLockException may result in undefined state!?
1774
     */
1775 37
    public function merge($entity)
1776
    {
1777 37
        $visited = array();
1778
1779 37
        return $this->doMerge($entity, $visited);
1780
    }
1781
1782
    /**
1783
     * Executes a merge operation on an entity.
1784
     *
1785
     * @param object      $entity
1786
     * @param array       $visited
1787
     * @param object|null $prevManagedCopy
1788
     * @param array|null  $assoc
1789
     *
1790
     * @return object The managed copy of the entity.
1791
     *
1792
     * @throws OptimisticLockException If the entity uses optimistic locking through a version
1793
     *         attribute and the version check against the managed copy fails.
1794
     * @throws ORMInvalidArgumentException If the entity instance is NEW.
1795
     * @throws EntityNotFoundException
1796
     */
1797 37
    private function doMerge($entity, array &$visited, $prevManagedCopy = null, $assoc = null)
1798
    {
1799 37
        $oid = spl_object_hash($entity);
1800
1801 37
        if (isset($visited[$oid])) {
1802 4
            $managedCopy = $visited[$oid];
1803
1804 4
            if ($prevManagedCopy !== null) {
1805 4
                $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy);
0 ignored issues
show
Bug introduced by
It seems like $assoc defined by parameter $assoc on line 1797 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...
1806
            }
1807
1808 4
            return $managedCopy;
1809
        }
1810
1811 37
        $class = $this->em->getClassMetadata(get_class($entity));
1812
1813
        // First we assume DETACHED, although it can still be NEW but we can avoid
1814
        // an extra db-roundtrip this way. If it is not MANAGED but has an identity,
1815
        // we need to fetch it from the db anyway in order to merge.
1816
        // MANAGED entities are ignored by the merge operation.
1817 37
        $managedCopy = $entity;
1818
1819 37
        if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) {
1820
            // Try to look the entity up in the identity map.
1821 36
            $id = $class->getIdentifierValues($entity);
1822
1823
            // If there is no ID, it is actually NEW.
1824 36
            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...
1825 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...
1826
1827 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...
1828
            } else {
1829 32
                $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...
1830 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...
1831 32
                    : $id;
1832
1833 32
                $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 1897 which is incompatible with the return type documented by Doctrine\ORM\UnitOfWork::doMerge of type object.
Loading history...
1834
1835 32
                if ($managedCopy) {
1836
                    // We have the entity in-memory already, just make sure its not removed.
1837 11
                    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 1833 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...
1838 11
                        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 1833 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...
1839
                    }
1840
                } else {
1841
                    // We need to fetch the managed copy in order to merge.
1842 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...
1843
                }
1844
1845 32
                if ($managedCopy === null) {
1846
                    // If the identifier is ASSIGNED, it is NEW, otherwise an error
1847
                    // since the managed entity was not found.
1848 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...
1849 1
                        throw EntityNotFoundException::fromClassNameAndIdentifier(
1850 1
                            $class->getName(),
1851 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...
1852
                        );
1853
                    }
1854
1855 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...
1856 1
                    $class->setIdentifierValues($managedCopy, $id);
1857
1858 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...
1859
                }
1860
            }
1861
1862 35
            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 1833 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...
1863 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...
1864 4
                $managedCopyVersion = $reflField->getValue($managedCopy);
1865 4
                $entityVersion      = $reflField->getValue($entity);
1866
1867
                // Throw exception if versions don't match.
1868 4
                if ($managedCopyVersion != $entityVersion) {
1869 1
                    throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion);
1870
                }
1871
            }
1872
1873 34
            $visited[$oid] = $managedCopy; // mark visited
1874
1875 34
            if ($this->isLoaded($entity)) {
1876 27
                if ($managedCopy instanceof Proxy && ! $managedCopy->__isInitialized()) {
1877 4
                    $managedCopy->__load();
1878
                }
1879
1880 27
                $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy);
0 ignored issues
show
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1833 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...
1881
            }
1882
1883 34
            if ($class->isChangeTrackingDeferredExplicit()) {
1884
                $this->scheduleForDirtyCheck($entity);
1885
            }
1886
        }
1887
1888 35
        if ($prevManagedCopy !== null) {
1889 6
            $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy);
0 ignored issues
show
Bug introduced by
It seems like $assoc defined by parameter $assoc on line 1797 can also be of type null; however, Doctrine\ORM\UnitOfWork:...ationWithMergedEntity() does only seem to accept array, maybe add an additional type check?

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

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

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like $managedCopy defined by $this->tryGetById($flatI...$class->rootEntityName) on line 1833 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...
1890
        }
1891
1892
        // Mark the managed copy visited as well
1893 35
        $visited[spl_object_hash($managedCopy)] = $managedCopy;
1894
1895 35
        $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 1833 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...
1896
1897 35
        return $managedCopy;
1898
    }
1899
1900
    /**
1901
     * Tests if an entity is loaded - must either be a loaded proxy or not a proxy
1902
     *
1903
     * @param object $entity
1904
     *
1905
     * @return bool
1906
     */
1907 35
    private function isLoaded($entity)
1908
    {
1909 35
        return !($entity instanceof Proxy) || $entity->__isInitialized();
1910
    }
1911
1912
    /**
1913
     * Sets/adds associated managed copies into the previous entity's association field
1914
     *
1915
     * @param object $entity
1916
     * @param array  $association
1917
     * @param object $previousManagedCopy
1918
     * @param object $managedCopy
1919
     *
1920
     * @return void
1921
     */
1922 6
    private function updateAssociationWithMergedEntity($entity, array $association, $previousManagedCopy, $managedCopy)
1923
    {
1924 6
        $assocField = $association['fieldName'];
1925 6
        $prevClass  = $this->em->getClassMetadata(get_class($previousManagedCopy));
1926
1927 6
        if ($association['type'] & ClassMetadata::TO_ONE) {
1928 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...
1929
1930 6
            return;
1931
        }
1932
1933 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...
1934 1
        $value[] = $managedCopy;
1935
1936 1
        if ($association['type'] == ClassMetadata::ONE_TO_MANY) {
1937 1
            $class = $this->em->getClassMetadata(get_class($entity));
1938
1939 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...
1940
        }
1941 1
    }
1942
1943
    /**
1944
     * Detaches an entity from the persistence management. It's persistence will
1945
     * no longer be managed by Doctrine.
1946
     *
1947
     * @param object $entity The entity to detach.
1948
     *
1949
     * @return void
1950
     */
1951 10
    public function detach($entity)
1952
    {
1953 10
        $visited = array();
1954
1955 10
        $this->doDetach($entity, $visited);
1956 10
    }
1957
1958
    /**
1959
     * Executes a detach operation on the given entity.
1960
     *
1961
     * @param object  $entity
1962
     * @param array   $visited
1963
     * @param boolean $noCascade if true, don't cascade detach operation.
1964
     *
1965
     * @return void
1966
     */
1967 12
    private function doDetach($entity, array &$visited, $noCascade = false)
1968
    {
1969 12
        $oid = spl_object_hash($entity);
1970
1971 12
        if (isset($visited[$oid])) {
1972
            return; // Prevent infinite recursion
1973
        }
1974
1975 12
        $visited[$oid] = $entity; // mark visited
1976
1977 12
        switch ($this->getEntityState($entity, self::STATE_DETACHED)) {
1978 12
            case self::STATE_MANAGED:
1979 10
                if ($this->isInIdentityMap($entity)) {
1980 9
                    $this->removeFromIdentityMap($entity);
1981
                }
1982
1983
                unset(
1984 10
                    $this->entityInsertions[$oid],
1985 10
                    $this->entityUpdates[$oid],
1986 10
                    $this->entityDeletions[$oid],
1987 10
                    $this->entityIdentifiers[$oid],
1988 10
                    $this->entityStates[$oid],
1989 10
                    $this->originalEntityData[$oid]
1990
                );
1991 10
                break;
1992 3
            case self::STATE_NEW:
1993 3
            case self::STATE_DETACHED:
1994 3
                return;
1995
        }
1996
1997 10
        if ( ! $noCascade) {
1998 10
            $this->cascadeDetach($entity, $visited);
1999
        }
2000 10
    }
2001
2002
    /**
2003
     * Refreshes the state of the given entity from the database, overwriting
2004
     * any local, unpersisted changes.
2005
     *
2006
     * @param object $entity The entity to refresh.
2007
     *
2008
     * @return void
2009
     *
2010
     * @throws InvalidArgumentException If the entity is not MANAGED.
2011
     */
2012 15
    public function refresh($entity)
2013
    {
2014 15
        $visited = array();
2015
2016 15
        $this->doRefresh($entity, $visited);
2017 15
    }
2018
2019
    /**
2020
     * Executes a refresh operation on an entity.
2021
     *
2022
     * @param object $entity  The entity to refresh.
2023
     * @param array  $visited The already visited entities during cascades.
2024
     *
2025
     * @return void
2026
     *
2027
     * @throws ORMInvalidArgumentException If the entity is not MANAGED.
2028
     */
2029 15
    private function doRefresh($entity, array &$visited)
2030
    {
2031 15
        $oid = spl_object_hash($entity);
2032
2033 15
        if (isset($visited[$oid])) {
2034
            return; // Prevent infinite recursion
2035
        }
2036
2037 15
        $visited[$oid] = $entity; // mark visited
2038
2039 15
        $class = $this->em->getClassMetadata(get_class($entity));
2040
2041 15
        if ($this->getEntityState($entity) !== self::STATE_MANAGED) {
2042
            throw ORMInvalidArgumentException::entityNotManaged($entity);
2043
        }
2044
2045 15
        $this->getEntityPersister($class->name)->refresh(
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2067
            function ($assoc) { return $assoc['isCascadeRefresh']; }
2068
        );
2069
2070 15
        foreach ($associationMappings as $assoc) {
2071
            $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...
2072
2073
            switch (true) {
2074
                case ($relatedEntities instanceof PersistentCollection):
2075
                    // Unwrap so that foreach() does not initialize
2076
                    $relatedEntities = $relatedEntities->unwrap();
2077
                    // break; is commented intentionally!
2078
2079
                case ($relatedEntities instanceof Collection):
2080
                case (is_array($relatedEntities)):
2081
                    foreach ($relatedEntities as $relatedEntity) {
2082
                        $this->doRefresh($relatedEntity, $visited);
2083
                    }
2084
                    break;
2085
2086
                case ($relatedEntities !== null):
2087
                    $this->doRefresh($relatedEntities, $visited);
2088
                    break;
2089
2090
                default:
2091
                    // Do nothing
2092
            }
2093
        }
2094 15
    }
2095
2096
    /**
2097
     * Cascades a detach operation to associated entities.
2098
     *
2099
     * @param object $entity
2100
     * @param array  $visited
2101
     *
2102
     * @return void
2103
     */
2104 10
    private function cascadeDetach($entity, array &$visited)
2105
    {
2106 10
        $class = $this->em->getClassMetadata(get_class($entity));
2107
2108 10
        $associationMappings = array_filter(
2109 10
            $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...
2110
            function ($assoc) { return $assoc['isCascadeDetach']; }
2111
        );
2112
2113 10
        foreach ($associationMappings as $assoc) {
2114 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...
2115
2116
            switch (true) {
2117 3
                case ($relatedEntities instanceof PersistentCollection):
2118
                    // Unwrap so that foreach() does not initialize
2119 2
                    $relatedEntities = $relatedEntities->unwrap();
2120
                    // break; is commented intentionally!
2121
2122
                case ($relatedEntities instanceof Collection):
2123
                case (is_array($relatedEntities)):
2124 3
                    foreach ($relatedEntities as $relatedEntity) {
2125 1
                        $this->doDetach($relatedEntity, $visited);
2126
                    }
2127 3
                    break;
2128
2129
                case ($relatedEntities !== null):
2130
                    $this->doDetach($relatedEntities, $visited);
2131
                    break;
2132
2133 3
                default:
2134
                    // Do nothing
2135
            }
2136
        }
2137 10
    }
2138
2139
    /**
2140
     * Cascades a merge operation to associated entities.
2141
     *
2142
     * @param object $entity
2143
     * @param object $managedCopy
2144
     * @param array  $visited
2145
     *
2146
     * @return void
2147
     */
2148 35
    private function cascadeMerge($entity, $managedCopy, array &$visited)
2149
    {
2150 35
        $class = $this->em->getClassMetadata(get_class($entity));
2151
2152 35
        $associationMappings = array_filter(
2153 35
            $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...
2154
            function ($assoc) { return $assoc['isCascadeMerge']; }
2155
        );
2156
2157 35
        foreach ($associationMappings as $assoc) {
2158 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...
2159
2160 15
            if ($relatedEntities instanceof Collection) {
2161 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...
2162 1
                    continue;
2163
                }
2164
2165 8
                if ($relatedEntities instanceof PersistentCollection) {
2166
                    // Unwrap so that foreach() does not initialize
2167 5
                    $relatedEntities = $relatedEntities->unwrap();
2168
                }
2169
2170 8
                foreach ($relatedEntities as $relatedEntity) {
2171 8
                    $this->doMerge($relatedEntity, $visited, $managedCopy, $assoc);
2172
                }
2173 7
            } else if ($relatedEntities !== null) {
2174 14
                $this->doMerge($relatedEntities, $visited, $managedCopy, $assoc);
2175
            }
2176
        }
2177 35
    }
2178
2179
    /**
2180
     * Cascades the save operation to associated entities.
2181
     *
2182
     * @param object $entity
2183
     * @param array  $visited
2184
     *
2185
     * @return void
2186
     */
2187 1006
    private function cascadePersist($entity, array &$visited)
2188
    {
2189 1006
        $class = $this->em->getClassMetadata(get_class($entity));
2190
2191 1006
        $associationMappings = array_filter(
2192 1006
            $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...
2193
            function ($assoc) { return $assoc['isCascadePersist']; }
2194
        );
2195
2196 1006
        foreach ($associationMappings as $assoc) {
2197 643
            $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...
2198
2199
            switch (true) {
2200 643
                case ($relatedEntities instanceof PersistentCollection):
2201
                    // Unwrap so that foreach() does not initialize
2202 20
                    $relatedEntities = $relatedEntities->unwrap();
2203
                    // break; is commented intentionally!
2204
2205
                case ($relatedEntities instanceof Collection):
2206 583
                case (is_array($relatedEntities)):
2207 548
                    if (($assoc['type'] & ClassMetadata::TO_MANY) <= 0) {
2208 3
                        throw ORMInvalidArgumentException::invalidAssociation(
2209 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...
2210
                            $assoc,
2211
                            $relatedEntities
2212
                        );
2213
                    }
2214
2215 545
                    foreach ($relatedEntities as $relatedEntity) {
2216 277
                        $this->doPersist($relatedEntity, $visited);
2217
                    }
2218
2219 545
                    break;
2220
2221 573
                case ($relatedEntities !== null):
2222 246
                    if (! $relatedEntities instanceof $assoc['targetEntity']) {
2223 4
                        throw ORMInvalidArgumentException::invalidAssociation(
2224 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...
2225
                            $assoc,
2226
                            $relatedEntities
2227
                        );
2228
                    }
2229
2230 242
                    $this->doPersist($relatedEntities, $visited);
2231 242
                    break;
2232
2233 637
                default:
2234
                    // Do nothing
2235
            }
2236
        }
2237 999
    }
2238
2239
    /**
2240
     * Cascades the delete operation to associated entities.
2241
     *
2242
     * @param object $entity
2243
     * @param array  $visited
2244
     *
2245
     * @return void
2246
     */
2247 64
    private function cascadeRemove($entity, array &$visited)
2248
    {
2249 64
        $class = $this->em->getClassMetadata(get_class($entity));
2250
2251 64
        $associationMappings = array_filter(
2252 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...
2253
            function ($assoc) { return $assoc['isCascadeRemove']; }
2254
        );
2255
2256 64
        $entitiesToCascade = array();
2257
2258 64
        foreach ($associationMappings as $assoc) {
2259 26
            if ($entity instanceof Proxy && !$entity->__isInitialized__) {
0 ignored issues
show
Bug introduced by
Accessing __isInitialized__ on the interface Doctrine\ORM\Proxy\Proxy suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
2264
2265
            switch (true) {
2266 26
                case ($relatedEntities instanceof Collection):
2267 19
                case (is_array($relatedEntities)):
2268
                    // If its a PersistentCollection initialization is intended! No unwrap!
2269 20
                    foreach ($relatedEntities as $relatedEntity) {
2270 10
                        $entitiesToCascade[] = $relatedEntity;
2271
                    }
2272 20
                    break;
2273
2274 19
                case ($relatedEntities !== null):
2275 7
                    $entitiesToCascade[] = $relatedEntities;
2276 7
                    break;
2277
2278 26
                default:
2279
                    // Do nothing
2280
            }
2281
        }
2282
2283 64
        foreach ($entitiesToCascade as $relatedEntity) {
2284 16
            $this->doRemove($relatedEntity, $visited);
2285
        }
2286 64
    }
2287
2288
    /**
2289
     * Acquire a lock on the given entity.
2290
     *
2291
     * @param object $entity
2292
     * @param int    $lockMode
2293
     * @param int    $lockVersion
2294
     *
2295
     * @return void
2296
     *
2297
     * @throws ORMInvalidArgumentException
2298
     * @throws TransactionRequiredException
2299
     * @throws OptimisticLockException
2300
     */
2301 11
    public function lock($entity, $lockMode, $lockVersion = null)
2302
    {
2303 11
        if ($entity === null) {
2304 1
            throw new \InvalidArgumentException("No entity passed to UnitOfWork#lock().");
2305
        }
2306
2307 10
        if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) {
2308 1
            throw ORMInvalidArgumentException::entityNotManaged($entity);
2309
        }
2310
2311 9
        $class = $this->em->getClassMetadata(get_class($entity));
2312
2313
        switch (true) {
2314 9
            case LockMode::OPTIMISTIC === $lockMode:
2315 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...
2316 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...
2317
                }
2318
2319 4
                if ($lockVersion === null) {
2320
                    return;
2321
                }
2322
2323 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...
2324 1
                    $entity->__load();
2325
                }
2326
2327 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...
2328
2329 4
                if ($entityVersion != $lockVersion) {
2330 2
                    throw OptimisticLockException::lockFailedVersionMismatch($entity, $lockVersion, $entityVersion);
2331
                }
2332
2333 2
                break;
2334
2335 3
            case LockMode::NONE === $lockMode:
2336 3
            case LockMode::PESSIMISTIC_READ === $lockMode:
2337 1
            case LockMode::PESSIMISTIC_WRITE === $lockMode:
2338 3
                if (!$this->em->getConnection()->isTransactionActive()) {
2339 2
                    throw TransactionRequiredException::transactionRequired();
2340
                }
2341
2342 1
                $oid = spl_object_hash($entity);
2343
2344 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...
2345 1
                    array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
2346
                    $lockMode
2347
                );
2348 1
                break;
2349
2350
            default:
2351
                // Do nothing
2352
        }
2353 3
    }
2354
2355
    /**
2356
     * Gets the CommitOrderCalculator used by the UnitOfWork to order commits.
2357
     *
2358
     * @return \Doctrine\ORM\Internal\CommitOrderCalculator
2359
     */
2360 994
    public function getCommitOrderCalculator()
2361
    {
2362 994
        return new Internal\CommitOrderCalculator();
2363
    }
2364
2365
    /**
2366
     * Clears the UnitOfWork.
2367
     *
2368
     * @param string|null $entityName if given, only entities of this type will get detached.
2369
     *
2370
     * @return void
2371
     */
2372 1208
    public function clear($entityName = null)
2373
    {
2374 1208
        if ($entityName === null) {
2375 1208
            $this->identityMap =
2376 1208
            $this->entityIdentifiers =
2377 1208
            $this->originalEntityData =
2378 1208
            $this->entityChangeSets =
2379 1208
            $this->entityStates =
2380 1208
            $this->scheduledForSynchronization =
2381 1208
            $this->entityInsertions =
2382 1208
            $this->entityUpdates =
2383 1208
            $this->entityDeletions =
2384 1208
            $this->collectionDeletions =
2385 1208
            $this->collectionUpdates =
2386 1208
            $this->extraUpdates =
2387 1208
            $this->readOnlyObjects =
2388 1208
            $this->visitedCollections =
2389 1208
            $this->orphanRemovals = array();
2390
        } else {
2391 2
            $visited = array();
2392
2393 2
            foreach ($this->identityMap as $className => $entities) {
2394 2
                if ($className !== $entityName) {
2395 1
                    continue;
2396
                }
2397
2398 2
                foreach ($entities as $entity) {
2399 2
                    $this->doDetach($entity, $visited, false);
2400
                }
2401
            }
2402
        }
2403
2404 1208
        if ($this->evm->hasListeners(Events::onClear)) {
2405 7
            $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em, $entityName));
2406
        }
2407 1208
    }
2408
2409
    /**
2410
     * INTERNAL:
2411
     * Schedules an orphaned entity for removal. The remove() operation will be
2412
     * invoked on that entity at the beginning of the next commit of this
2413
     * UnitOfWork.
2414
     *
2415
     * @ignore
2416
     *
2417
     * @param object $entity
2418
     *
2419
     * @return void
2420
     */
2421 17
    public function scheduleOrphanRemoval($entity)
2422
    {
2423 17
        $this->orphanRemovals[spl_object_hash($entity)] = $entity;
2424 17
    }
2425
2426
    /**
2427
     * INTERNAL:
2428
     * Cancels a previously scheduled orphan removal.
2429
     *
2430
     * @ignore
2431
     *
2432
     * @param object $entity
2433
     *
2434
     * @return void
2435
     */
2436 110
    public function cancelOrphanRemoval($entity)
2437
    {
2438 110
        unset($this->orphanRemovals[spl_object_hash($entity)]);
2439 110
    }
2440
2441
    /**
2442
     * INTERNAL:
2443
     * Schedules a complete collection for removal when this UnitOfWork commits.
2444
     *
2445
     * @param PersistentCollection $coll
2446
     *
2447
     * @return void
2448
     */
2449 13
    public function scheduleCollectionDeletion(PersistentCollection $coll)
2450
    {
2451 13
        $coid = spl_object_hash($coll);
2452
2453
        // TODO: if $coll is already scheduled for recreation ... what to do?
2454
        // Just remove $coll from the scheduled recreations?
2455 13
        if (isset($this->collectionUpdates[$coid])) {
2456
            unset($this->collectionUpdates[$coid]);
2457
        }
2458
2459 13
        $this->collectionDeletions[$coid] = $coll;
2460 13
    }
2461
2462
    /**
2463
     * @param PersistentCollection $coll
2464
     *
2465
     * @return bool
2466
     */
2467
    public function isCollectionScheduledForDeletion(PersistentCollection $coll)
2468
    {
2469
        return isset($this->collectionDeletions[spl_object_hash($coll)]);
2470
    }
2471
2472
    /**
2473
     * @param ClassMetadata $class
2474
     *
2475
     * @return \Doctrine\Common\Persistence\ObjectManagerAware|object
2476
     */
2477 655
    private function newInstance($class)
2478
    {
2479 655
        $entity = $class->newInstance();
2480
2481 655
        if ($entity instanceof \Doctrine\Common\Persistence\ObjectManagerAware) {
2482 4
            $entity->injectObjectManager($this->em, $class);
2483
        }
2484
2485 655
        return $entity;
2486
    }
2487
2488
    /**
2489
     * INTERNAL:
2490
     * Creates an entity. Used for reconstitution of persistent entities.
2491
     *
2492
     * Internal note: Highly performance-sensitive method.
2493
     *
2494
     * @ignore
2495
     *
2496
     * @param string $className The name of the entity class.
2497
     * @param array  $data      The data for the entity.
2498
     * @param array  $hints     Any hints to account for during reconstitution/lookup of the entity.
2499
     *
2500
     * @return object The managed entity instance.
2501
     *
2502
     * @todo Rename: getOrCreateEntity
2503
     */
2504 791
    public function createEntity($className, array $data, &$hints = array())
2505
    {
2506 791
        $class = $this->em->getClassMetadata($className);
2507
        //$isReadOnly = isset($hints[Query::HINT_READ_ONLY]);
2508
2509 791
        $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...
2510 791
        $idHash = implode(' ', $id);
2511
2512 791
        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...
2513 308
            $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...
2514 308
            $oid = spl_object_hash($entity);
2515
2516
            if (
2517 308
                isset($hints[Query::HINT_REFRESH])
2518 308
                && isset($hints[Query::HINT_REFRESH_ENTITY])
2519 308
                && ($unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY]) !== $entity
2520 308
                && $unmanagedProxy instanceof Proxy
2521 308
                && $this->isIdentifierEquals($unmanagedProxy, $entity)
2522
            ) {
2523
                // DDC-1238 - we have a managed instance, but it isn't the provided one.
2524
                // Therefore we clear its identifier. Also, we must re-fetch metadata since the
2525
                // refreshed object may be anything
2526
2527 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...
2528 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...
2529
                }
2530
2531 2
                return $unmanagedProxy;
2532
            }
2533
2534 306
            if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
2535 21
                $entity->__setInitialized(true);
2536
2537 21
                $overrideLocalValues = true;
2538
2539 21
                if ($entity instanceof NotifyPropertyChanged) {
2540 21
                    $entity->addPropertyChangedListener($this);
2541
                }
2542
            } else {
2543 287
                $overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
2544
2545
                // If only a specific entity is set to refresh, check that it's the one
2546 287
                if (isset($hints[Query::HINT_REFRESH_ENTITY])) {
2547 71
                    $overrideLocalValues = $hints[Query::HINT_REFRESH_ENTITY] === $entity;
2548
                }
2549
            }
2550
2551 306
            if ($overrideLocalValues) {
2552
                // inject ObjectManager upon refresh.
2553 110
                if ($entity instanceof ObjectManagerAware) {
2554 3
                    $entity->injectObjectManager($this->em, $class);
2555
                }
2556
2557 306
                $this->originalEntityData[$oid] = $data;
2558
            }
2559
        } else {
2560 652
            $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...
2561 652
            $oid    = spl_object_hash($entity);
2562
2563 652
            $this->entityIdentifiers[$oid]  = $id;
2564 652
            $this->entityStates[$oid]       = self::STATE_MANAGED;
2565 652
            $this->originalEntityData[$oid] = $data;
2566
2567 652
            $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...
2568
2569 652
            if ($entity instanceof NotifyPropertyChanged) {
2570 2
                $entity->addPropertyChangedListener($this);
2571
            }
2572
2573 652
            $overrideLocalValues = true;
2574
        }
2575
2576 790
        if ( ! $overrideLocalValues) {
2577 217
            return $entity;
2578
        }
2579
2580 688
        foreach ($data as $field => $value) {
2581 688
            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...
2582 688
                $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...
2583
            }
2584
        }
2585
2586
        // Loading the entity right here, if its in the eager loading map get rid of it there.
2587 688
        unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
2588
2589 688
        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...
2590
            unset($this->eagerLoadingEntities[$class->rootEntityName]);
2591
        }
2592
2593
        // Properly initialize any unfetched associations, if partial objects are not allowed.
2594 688
        if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
2595 33
            return $entity;
2596
        }
2597
2598 655
        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...
2599
            // Check if the association is not among the fetch-joined associations already.
2600 577
            if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) {
2601 254
                continue;
2602
            }
2603
2604 555
            $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
2605
2606
            switch (true) {
2607 555
                case ($assoc['type'] & ClassMetadata::TO_ONE):
2608 475
                    if ( ! $assoc['isOwningSide']) {
2609
2610
                        // use the given entity association
2611 64
                        if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2612
2613 2
                            $this->originalEntityData[$oid][$field] = $data[$field];
2614
2615 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...
2616 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...
2617
2618 2
                            continue 2;
2619
                        }
2620
2621
                        // Inverse side of x-to-one can never be lazy
2622 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...
2623
2624 62
                        continue 2;
2625
                    }
2626
2627
                    // use the entity association
2628 475
                    if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
2629 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...
2630 37
                        $this->originalEntityData[$oid][$field] = $data[$field];
2631
2632 37
                        continue;
2633
                    }
2634
2635 468
                    $associatedId = array();
2636
2637
                    // TODO: Is this even computed right in all cases of composite keys?
2638 468
                    foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
2639 468
                        $joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
2640
2641 468
                        if ($joinColumnValue !== null) {
2642 280
                            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...
2643 11
                                $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
2644
                            } else {
2645 280
                                $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...
2646
                            }
2647 280
                        } 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...
2648 280
                            && 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...
2649
                        ) {
2650
                            // the missing key is part of target's entity primary key
2651 7
                            $associatedId = array();
2652 468
                            break;
2653
                        }
2654
                    }
2655
2656 468
                    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...
2657
                        // Foreign key is NULL
2658 280
                        $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...
2659 280
                        $this->originalEntityData[$oid][$field] = null;
2660
2661 280
                        continue;
2662
                    }
2663
2664 280
                    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...
2665 277
                        $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...
2666
                    }
2667
2668
                    // Foreign key is set
2669
                    // Check identity map first
2670
                    // FIXME: Can break easily with composite keys if join column values are in
2671
                    //        wrong order. The correct order is the one in ClassMetadata#identifier.
2672 280
                    $relatedIdHash = implode(' ', $associatedId);
2673
2674
                    switch (true) {
2675 280
                        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...
2676 163
                            $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...
2677
2678
                            // If this is an uninitialized proxy, we are deferring eager loads,
2679
                            // this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
2680
                            // then we can append this entity for eager loading!
2681 163
                            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...
2682 163
                                isset($hints[self::HINT_DEFEREAGERLOAD]) &&
2683 163
                                !$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...
2684 163
                                $newValue instanceof Proxy &&
2685 163
                                $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...
2686
2687
                                $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...
2688
                            }
2689
2690 163
                            break;
2691
2692 189
                        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...
2693
                            // If it might be a subtype, it can not be lazy. There isn't even
2694
                            // a way to solve this with deferred eager loading, which means putting
2695
                            // an entity with subclasses at a *-to-one location is really bad! (performance-wise)
2696 30
                            $newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, $associatedId);
2697 30
                            break;
2698
2699
                        default:
2700
                            switch (true) {
2701
                                // We are negating the condition here. Other cases will assume it is valid!
2702 160
                                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...
2703 153
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2704 153
                                    break;
2705
2706
                                // Deferred eager load only works for single identifier classes
2707 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...
2708
                                    // TODO: Is there a faster approach?
2709 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...
2710
2711 7
                                    $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
2712 7
                                    break;
2713
2714
                                default:
2715
                                    // TODO: This is very imperformant, ignore it?
2716
                                    $newValue = $this->em->find($assoc['targetEntity'], $associatedId);
2717
                                    break;
2718
                            }
2719
2720
                            // PERF: Inlined & optimized code from UnitOfWork#registerManaged()
2721 160
                            $newValueOid = spl_object_hash($newValue);
2722 160
                            $this->entityIdentifiers[$newValueOid] = $associatedId;
2723 160
                            $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...
2724
2725
                            if (
2726 160
                                $newValue instanceof NotifyPropertyChanged &&
2727 160
                                ( ! $newValue instanceof Proxy || $newValue->__isInitialized())
2728
                            ) {
2729
                                $newValue->addPropertyChangedListener($this);
2730
                            }
2731 160
                            $this->entityStates[$newValueOid] = self::STATE_MANAGED;
2732
                            // make sure that when an proxy is then finally loaded, $this->originalEntityData is set also!
2733 160
                            break;
2734
                    }
2735
2736 280
                    $this->originalEntityData[$oid][$field] = $newValue;
2737 280
                    $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...
2738
2739 280
                    if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
2740 49
                        $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...
2741 49
                        $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...
2742
                    }
2743
2744 280
                    break;
2745
2746
                default:
2747
                    // Ignore if its a cached collection
2748 478
                    if (isset($hints[Query::HINT_CACHE_ENABLED]) && $class->getFieldValue($entity, $field) instanceof PersistentCollection) {
2749
                        break;
2750
                    }
2751
2752
                    // use the given collection
2753 478
                    if (isset($data[$field]) && $data[$field] instanceof PersistentCollection) {
2754
2755 2
                        $data[$field]->setOwner($entity, $assoc);
2756
2757 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...
2758 2
                        $this->originalEntityData[$oid][$field] = $data[$field];
2759
2760 2
                        break;
2761
                    }
2762
2763
                    // Inject collection
2764 478
                    $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...
2765 478
                    $pColl->setOwner($entity, $assoc);
2766 478
                    $pColl->setInitialized(false);
2767
2768 478
                    $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...
2769 478
                    $reflField->setValue($entity, $pColl);
2770
2771 478
                    if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
2772 4
                        $this->loadCollection($pColl);
2773 4
                        $pColl->takeSnapshot();
2774
                    }
2775
2776 478
                    $this->originalEntityData[$oid][$field] = $pColl;
2777 555
                    break;
2778
            }
2779
        }
2780
2781 655
        if ($overrideLocalValues) {
2782
            // defer invoking of postLoad event to hydration complete step
2783 655
            $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...
2784
        }
2785
2786 655
        return $entity;
2787
    }
2788
2789
    /**
2790
     * @return void
2791
     */
2792 848
    public function triggerEagerLoads()
2793
    {
2794 848
        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...
2795 848
            return;
2796
        }
2797
2798
        // avoid infinite recursion
2799 7
        $eagerLoadingEntities       = $this->eagerLoadingEntities;
2800 7
        $this->eagerLoadingEntities = array();
2801
2802 7
        foreach ($eagerLoadingEntities as $entityName => $ids) {
2803 7
            if ( ! $ids) {
2804
                continue;
2805
            }
2806
2807 7
            $class = $this->em->getClassMetadata($entityName);
2808
2809 7
            $this->getEntityPersister($entityName)->loadAll(
2810 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...
2811
            );
2812
        }
2813 7
    }
2814
2815
    /**
2816
     * Initializes (loads) an uninitialized persistent collection of an entity.
2817
     *
2818
     * @param \Doctrine\ORM\PersistentCollection $collection The collection to initialize.
2819
     *
2820
     * @return void
2821
     *
2822
     * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733.
2823
     */
2824 142
    public function loadCollection(PersistentCollection $collection)
2825
    {
2826 142
        $assoc     = $collection->getMapping();
2827 142
        $persister = $this->getEntityPersister($assoc['targetEntity']);
2828
2829 142
        switch ($assoc['type']) {
2830 142
            case ClassMetadata::ONE_TO_MANY:
2831 75
                $persister->loadOneToManyCollection($assoc, $collection->getOwner(), $collection);
2832 75
                break;
2833
2834 76
            case ClassMetadata::MANY_TO_MANY:
2835 76
                $persister->loadManyToManyCollection($assoc, $collection->getOwner(), $collection);
2836 76
                break;
2837
        }
2838
2839 142
        $collection->setInitialized(true);
2840 142
    }
2841
2842
    /**
2843
     * Gets the identity map of the UnitOfWork.
2844
     *
2845
     * @return array
2846
     */
2847 2
    public function getIdentityMap()
2848
    {
2849 2
        return $this->identityMap;
2850
    }
2851
2852
    /**
2853
     * Gets the original data of an entity. The original data is the data that was
2854
     * present at the time the entity was reconstituted from the database.
2855
     *
2856
     * @param object $entity
2857
     *
2858
     * @return array
2859
     */
2860 114
    public function getOriginalEntityData($entity)
2861
    {
2862 114
        $oid = spl_object_hash($entity);
2863
2864 114
        if (isset($this->originalEntityData[$oid])) {
2865 111
            return $this->originalEntityData[$oid];
2866
        }
2867
2868 4
        return array();
2869
    }
2870
2871
    /**
2872
     * @ignore
2873
     *
2874
     * @param object $entity
2875
     * @param array  $data
2876
     *
2877
     * @return void
2878
     */
2879
    public function setOriginalEntityData($entity, array $data)
2880
    {
2881
        $this->originalEntityData[spl_object_hash($entity)] = $data;
2882
    }
2883
2884
    /**
2885
     * INTERNAL:
2886
     * Sets a property value of the original data array of an entity.
2887
     *
2888
     * @ignore
2889
     *
2890
     * @param string $oid
2891
     * @param string $property
2892
     * @param mixed  $value
2893
     *
2894
     * @return void
2895
     */
2896 307
    public function setOriginalEntityProperty($oid, $property, $value)
2897
    {
2898 307
        $this->originalEntityData[$oid][$property] = $value;
2899 307
    }
2900
2901
    /**
2902
     * Gets the identifier of an entity.
2903
     * The returned value is always an array of identifier values. If the entity
2904
     * has a composite identifier then the identifier values are in the same
2905
     * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames().
2906
     *
2907
     * @param object $entity
2908
     *
2909
     * @return array The identifier values.
2910
     */
2911 832
    public function getEntityIdentifier($entity)
2912
    {
2913 832
        return $this->entityIdentifiers[spl_object_hash($entity)];
2914
    }
2915
2916
    /**
2917
     * Processes an entity instance to extract their identifier values.
2918
     *
2919
     * @param object $entity The entity instance.
2920
     *
2921
     * @return mixed A scalar value.
2922
     *
2923
     * @throws \Doctrine\ORM\ORMInvalidArgumentException
2924
     */
2925 126
    public function getSingleIdentifierValue($entity)
2926
    {
2927 126
        $class = $this->em->getClassMetadata(get_class($entity));
2928
2929 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...
2930
            throw ORMInvalidArgumentException::invalidCompositeIdentifier();
2931
        }
2932
2933 126
        $values = $this->isInIdentityMap($entity)
2934 113
            ? $this->getEntityIdentifier($entity)
2935 126
            : $class->getIdentifierValues($entity);
2936
2937 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...
2938
    }
2939
2940
    /**
2941
     * Tries to find an entity with the given identifier in the identity map of
2942
     * this UnitOfWork.
2943
     *
2944
     * @param mixed  $id            The entity identifier to look for.
2945
     * @param string $rootClassName The name of the root class of the mapped entity hierarchy.
2946
     *
2947
     * @return object|bool Returns the entity with the specified identifier if it exists in
2948
     *                     this UnitOfWork, FALSE otherwise.
2949
     */
2950 507
    public function tryGetById($id, $rootClassName)
2951
    {
2952 507
        $idHash = implode(' ', (array) $id);
2953
2954 507
        if (isset($this->identityMap[$rootClassName][$idHash])) {
2955 74
            return $this->identityMap[$rootClassName][$idHash];
2956
        }
2957
2958 495
        return false;
2959
    }
2960
2961
    /**
2962
     * Schedules an entity for dirty-checking at commit-time.
2963
     *
2964
     * @param object $entity The entity to schedule for dirty-checking.
2965
     *
2966
     * @return void
2967
     *
2968
     * @todo Rename: scheduleForSynchronization
2969
     */
2970 5
    public function scheduleForDirtyCheck($entity)
2971
    {
2972 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...
2973
2974 5
        $this->scheduledForSynchronization[$rootClassName][spl_object_hash($entity)] = $entity;
2975 5
    }
2976
2977
    /**
2978
     * Checks whether the UnitOfWork has any pending insertions.
2979
     *
2980
     * @return boolean TRUE if this UnitOfWork has pending insertions, FALSE otherwise.
2981
     */
2982
    public function hasPendingInsertions()
2983
    {
2984
        return ! empty($this->entityInsertions);
2985
    }
2986
2987
    /**
2988
     * Calculates the size of the UnitOfWork. The size of the UnitOfWork is the
2989
     * number of entities in the identity map.
2990
     *
2991
     * @return integer
2992
     */
2993 1
    public function size()
2994
    {
2995
        $countArray = array_map(function ($item) { return count($item); }, $this->identityMap);
2996
2997 1
        return array_sum($countArray);
2998
    }
2999
3000
    /**
3001
     * Gets the EntityPersister for an Entity.
3002
     *
3003
     * @param string $entityName The name of the Entity.
3004
     *
3005
     * @return \Doctrine\ORM\Persisters\Entity\EntityPersister
3006
     */
3007 1056
    public function getEntityPersister($entityName)
3008
    {
3009 1056
        if (isset($this->persisters[$entityName])) {
3010 832
            return $this->persisters[$entityName];
3011
        }
3012
3013 1056
        $class = $this->em->getClassMetadata($entityName);
3014
3015 1056
        $class->customRepositoryClassName;
0 ignored issues
show
Bug introduced by
Accessing customRepositoryClassName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
3016
3017
        switch (true) {
3018 1056
            case ($class->customPersisterClassName !== null):
0 ignored issues
show
Bug introduced by
Accessing customPersisterClassName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

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

Available Fixes

  1. Adding an additional type check:

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

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

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

Available Fixes

  1. Adding an additional type check:

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

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
3020
                $persister = new $persister($this->em, $class);
3021
                break;
3022
3023 1056
            case ($class->isInheritanceTypeNone()):
3024 1019
                $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...
3025 1019
                break;
3026
3027 357
            case ($class->isInheritanceTypeSingleTable()):
3028 211
                $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...
3029 211
                break;
3030
3031 336
            case ($class->isInheritanceTypeJoined()):
3032 336
                $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...
3033 336
                break;
3034
3035
            default:
3036
                throw new \RuntimeException('No persister found for entity.');
3037
        }
3038
3039 1056
        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...
3040 118
            $persister = $this->em->getConfiguration()
3041 118
                ->getSecondLevelCacheConfiguration()
3042 118
                ->getCacheFactory()
3043 118
                ->buildCachedEntityPersister($this->em, $persister, $class);
3044
        }
3045
3046 1056
        $this->persisters[$entityName] = $persister;
3047
3048 1056
        return $this->persisters[$entityName];
3049
    }
3050
3051
    /**
3052
     * Gets a collection persister for a collection-valued association.
3053
     *
3054
     * @param array $association
3055
     *
3056
     * @return \Doctrine\ORM\Persisters\Collection\CollectionPersister
3057
     */
3058 561
    public function getCollectionPersister(array $association)
3059
    {
3060 561
        $role = isset($association['cache'])
3061 75
            ? $association['sourceEntity'] . '::' . $association['fieldName']
3062 561
            : $association['type'];
3063
3064 561
        if (isset($this->collectionPersisters[$role])) {
3065 441
            return $this->collectionPersisters[$role];
3066
        }
3067
3068 561
        if (!isset($association['persister'])) {
3069 561
            $association['persister'] = (ClassMetadata::ONE_TO_MANY === $association['type'])
3070 401
                ? 'Doctrine\ORM\Persisters\Collection\OneToManyPersister'
3071 369
                : 'Doctrine\ORM\Persisters\Collection\ManyToManyPersister';
3072
        }
3073
3074 561
        $persister = $association['persister'];
3075 561
        $persister = new $persister($this->em);
3076
3077 561
        if ($this->hasCache && isset($association['cache'])) {
3078 74
            $persister = $this->em->getConfiguration()
3079 74
                ->getSecondLevelCacheConfiguration()
3080 74
                ->getCacheFactory()
3081 74
                ->buildCachedCollectionPersister($this->em, $persister, $association);
3082
        }
3083
3084 561
        $this->collectionPersisters[$role] = $persister;
3085
3086 561
        return $this->collectionPersisters[$role];
3087
    }
3088
3089
    /**
3090
     * INTERNAL:
3091
     * Registers an entity as managed.
3092
     *
3093
     * @param object $entity The entity.
3094
     * @param array  $id     The identifier values.
3095
     * @param array  $data   The original entity data.
3096
     *
3097
     * @return void
3098
     */
3099 200
    public function registerManaged($entity, array $id, array $data)
3100
    {
3101 200
        $oid = spl_object_hash($entity);
3102
3103 200
        $this->entityIdentifiers[$oid]  = $id;
3104 200
        $this->entityStates[$oid]       = self::STATE_MANAGED;
3105 200
        $this->originalEntityData[$oid] = $data;
3106
3107 200
        $this->addToIdentityMap($entity);
3108
3109 199
        if ($entity instanceof NotifyPropertyChanged && ( ! $entity instanceof Proxy || $entity->__isInitialized())) {
3110 2
            $entity->addPropertyChangedListener($this);
3111
        }
3112 199
    }
3113
3114
    /**
3115
     * INTERNAL:
3116
     * Clears the property changeset of the entity with the given OID.
3117
     *
3118
     * @param string $oid The entity's OID.
3119
     *
3120
     * @return void
3121
     */
3122
    public function clearEntityChangeSet($oid)
3123
    {
3124
        $this->entityChangeSets[$oid] = array();
3125
    }
3126
3127
    /* PropertyChangedListener implementation */
3128
3129
    /**
3130
     * Notifies this UnitOfWork of a property change in an entity.
3131
     *
3132
     * @param object $entity       The entity that owns the property.
3133
     * @param string $propertyName The name of the property that changed.
3134
     * @param mixed  $oldValue     The old value of the property.
3135
     * @param mixed  $newValue     The new value of the property.
3136
     *
3137
     * @return void
3138
     */
3139 3
    public function propertyChanged($entity, $propertyName, $oldValue, $newValue)
3140
    {
3141 3
        $oid   = spl_object_hash($entity);
3142 3
        $class = $this->em->getClassMetadata(get_class($entity));
3143
3144 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...
3145
3146 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...
3147 1
            return; // ignore non-persistent fields
3148
        }
3149
3150
        // Update changeset and mark entity for synchronization
3151 3
        $this->entityChangeSets[$oid][$propertyName] = array($oldValue, $newValue);
3152
3153 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...
3154 3
            $this->scheduleForDirtyCheck($entity);
3155
        }
3156 3
    }
3157
3158
    /**
3159
     * Gets the currently scheduled entity insertions in this UnitOfWork.
3160
     *
3161
     * @return array
3162
     */
3163 2
    public function getScheduledEntityInsertions()
3164
    {
3165 2
        return $this->entityInsertions;
3166
    }
3167
3168
    /**
3169
     * Gets the currently scheduled entity updates in this UnitOfWork.
3170
     *
3171
     * @return array
3172
     */
3173 2
    public function getScheduledEntityUpdates()
3174
    {
3175 2
        return $this->entityUpdates;
3176
    }
3177
3178
    /**
3179
     * Gets the currently scheduled entity deletions in this UnitOfWork.
3180
     *
3181
     * @return array
3182
     */
3183 1
    public function getScheduledEntityDeletions()
3184
    {
3185 1
        return $this->entityDeletions;
3186
    }
3187
3188
    /**
3189
     * Gets the currently scheduled complete collection deletions
3190
     *
3191
     * @return array
3192
     */
3193 1
    public function getScheduledCollectionDeletions()
3194
    {
3195 1
        return $this->collectionDeletions;
3196
    }
3197
3198
    /**
3199
     * Gets the currently scheduled collection inserts, updates and deletes.
3200
     *
3201
     * @return array
3202
     */
3203
    public function getScheduledCollectionUpdates()
3204
    {
3205
        return $this->collectionUpdates;
3206
    }
3207
3208
    /**
3209
     * Helper method to initialize a lazy loading proxy or persistent collection.
3210
     *
3211
     * @param object $obj
3212
     *
3213
     * @return void
3214
     */
3215 2
    public function initializeObject($obj)
3216
    {
3217 2
        if ($obj instanceof Proxy) {
3218 1
            $obj->__load();
3219
3220 1
            return;
3221
        }
3222
3223 1
        if ($obj instanceof PersistentCollection) {
3224 1
            $obj->initialize();
3225
        }
3226 1
    }
3227
3228
    /**
3229
     * Helper method to show an object as string.
3230
     *
3231
     * @param object $obj
3232
     *
3233
     * @return string
3234
     */
3235 1
    private static function objToStr($obj)
3236
    {
3237 1
        return method_exists($obj, '__toString') ? (string) $obj : get_class($obj).'@'.spl_object_hash($obj);
3238
    }
3239
3240
    /**
3241
     * Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit().
3242
     *
3243
     * This operation cannot be undone as some parts of the UnitOfWork now keep gathering information
3244
     * on this object that might be necessary to perform a correct update.
3245
     *
3246
     * @param object $object
3247
     *
3248
     * @return void
3249
     *
3250
     * @throws ORMInvalidArgumentException
3251
     */
3252 6
    public function markReadOnly($object)
3253
    {
3254 6
        if ( ! is_object($object) || ! $this->isInIdentityMap($object)) {
3255 1
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3256
        }
3257
3258 5
        $this->readOnlyObjects[spl_object_hash($object)] = true;
3259 5
    }
3260
3261
    /**
3262
     * Is this entity read only?
3263
     *
3264
     * @param object $object
3265
     *
3266
     * @return bool
3267
     *
3268
     * @throws ORMInvalidArgumentException
3269
     */
3270 3
    public function isReadOnly($object)
3271
    {
3272 3
        if ( ! is_object($object)) {
3273
            throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object);
3274
        }
3275
3276 3
        return isset($this->readOnlyObjects[spl_object_hash($object)]);
3277
    }
3278
3279
    /**
3280
     * Perform whatever processing is encapsulated here after completion of the transaction.
3281
     */
3282 984
    private function afterTransactionComplete()
3283
    {
3284
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3285 88
            $persister->afterTransactionComplete();
3286 984
        });
3287 984
    }
3288
3289
    /**
3290
     * Perform whatever processing is encapsulated here after completion of the rolled-back.
3291
     */
3292
    private function afterTransactionRolledBack()
3293
    {
3294 16
        $this->performCallbackOnCachedPersister(function (CachedPersister $persister) {
3295 3
            $persister->afterTransactionRolledBack();
3296 16
        });
3297 16
    }
3298
3299
    /**
3300
     * Performs an action after the transaction.
3301
     *
3302
     * @param callable $callback
3303
     */
3304 994
    private function performCallbackOnCachedPersister(callable $callback)
3305
    {
3306 994
        if ( ! $this->hasCache) {
3307 906
            return;
3308
        }
3309
3310 88
        foreach (array_merge($this->persisters, $this->collectionPersisters) as $persister) {
3311 88
            if ($persister instanceof CachedPersister) {
3312 88
                $callback($persister);
3313
            }
3314
        }
3315 88
    }
3316
3317 998
    private function dispatchOnFlushEvent()
3318
    {
3319 998
        if ($this->evm->hasListeners(Events::onFlush)) {
3320 4
            $this->evm->dispatchEvent(Events::onFlush, new OnFlushEventArgs($this->em));
3321
        }
3322 998
    }
3323
3324 988
    private function dispatchPostFlushEvent()
3325
    {
3326 988
        if ($this->evm->hasListeners(Events::postFlush)) {
3327 5
            $this->evm->dispatchEvent(Events::postFlush, new PostFlushEventArgs($this->em));
3328
        }
3329 987
    }
3330
3331
    /**
3332
     * Verifies if two given entities actually are the same based on identifier comparison
3333
     *
3334
     * @param object $entity1
3335
     * @param object $entity2
3336
     *
3337
     * @return bool
3338
     */
3339 14
    private function isIdentifierEquals($entity1, $entity2)
3340
    {
3341 14
        if ($entity1 === $entity2) {
3342
            return true;
3343
        }
3344
3345 14
        $class = $this->em->getClassMetadata(get_class($entity1));
3346
3347 14
        if ($class !== $this->em->getClassMetadata(get_class($entity2))) {
3348 11
            return false;
3349
        }
3350
3351 3
        $oid1 = spl_object_hash($entity1);
3352 3
        $oid2 = spl_object_hash($entity2);
3353
3354 3
        $id1 = isset($this->entityIdentifiers[$oid1])
3355 3
            ? $this->entityIdentifiers[$oid1]
3356 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...
3357 3
        $id2 = isset($this->entityIdentifiers[$oid2])
3358 3
            ? $this->entityIdentifiers[$oid2]
3359 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...
3360
3361 3
        return $id1 === $id2 || implode(' ', $id1) === implode(' ', $id2);
3362
    }
3363
3364
    /**
3365
     * @param object $entity
3366
     * @param object $managedCopy
3367
     *
3368
     * @throws ORMException
3369
     * @throws OptimisticLockException
3370
     * @throws TransactionRequiredException
3371
     */
3372 27
    private function mergeEntityStateIntoManagedCopy($entity, $managedCopy)
3373
    {
3374 27
        $class = $this->em->getClassMetadata(get_class($entity));
3375
3376 27
        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...
3377 27
            $name = $prop->name;
3378
3379 27
            $prop->setAccessible(true);
3380
3381 27
            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...
3382 27
                if ( ! $class->isIdentifier($name)) {
3383 27
                    $prop->setValue($managedCopy, $prop->getValue($entity));
3384
                }
3385
            } else {
3386 26
                $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...
3387
3388 26
                if ($assoc2['type'] & ClassMetadata::TO_ONE) {
3389 22
                    $other = $prop->getValue($entity);
3390 22
                    if ($other === null) {
3391 10
                        $prop->setValue($managedCopy, null);
3392
                    } else {
3393 15
                        if ($other instanceof Proxy && !$other->__isInitialized()) {
3394
                            // do not merge fields marked lazy that have not been fetched.
3395 3
                            continue;
3396
                        }
3397
3398 12
                        if ( ! $assoc2['isCascadeMerge']) {
3399 6
                            if ($this->getEntityState($other) === self::STATE_DETACHED) {
3400 3
                                $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']);
3401 3
                                $relatedId   = $targetClass->getIdentifierValues($other);
3402
3403 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...
3404 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...
3405
                                } else {
3406 1
                                    $other = $this->em->getProxyFactory()->getProxy(
3407 1
                                        $assoc2['targetEntity'],
3408
                                        $relatedId
3409
                                    );
3410 1
                                    $this->registerManaged($other, $relatedId, array());
3411
                                }
3412
                            }
3413
3414 19
                            $prop->setValue($managedCopy, $other);
3415
                        }
3416
                    }
3417
                } else {
3418 14
                    $mergeCol = $prop->getValue($entity);
3419
3420 14
                    if ($mergeCol instanceof PersistentCollection && ! $mergeCol->isInitialized()) {
3421
                        // do not merge fields marked lazy that have not been fetched.
3422
                        // keep the lazy persistent collection of the managed copy.
3423 3
                        continue;
3424
                    }
3425
3426 13
                    $managedCol = $prop->getValue($managedCopy);
3427
3428 13
                    if ( ! $managedCol) {
3429 3
                        $managedCol = new PersistentCollection(
3430 3
                            $this->em,
3431 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...
3432 3
                            new ArrayCollection
3433
                        );
3434 3
                        $managedCol->setOwner($managedCopy, $assoc2);
3435 3
                        $prop->setValue($managedCopy, $managedCol);
3436
3437 3
                        $this->originalEntityData[spl_object_hash($entity)][$name] = $managedCol;
3438
                    }
3439
3440 13
                    if ($assoc2['isCascadeMerge']) {
3441 8
                        $managedCol->initialize();
3442
3443
                        // clear and set dirty a managed collection if its not also the same collection to merge from.
3444 8
                        if ( ! $managedCol->isEmpty() && $managedCol !== $mergeCol) {
3445 1
                            $managedCol->unwrap()->clear();
3446 1
                            $managedCol->setDirty(true);
3447
3448 1
                            if ($assoc2['isOwningSide']
3449 1
                                && $assoc2['type'] == ClassMetadata::MANY_TO_MANY
3450 1
                                && $class->isChangeTrackingNotify()
3451
                            ) {
3452
                                $this->scheduleForDirtyCheck($managedCopy);
3453
                            }
3454
                        }
3455
                    }
3456
                }
3457
            }
3458
3459 27
            if ($class->isChangeTrackingNotify()) {
3460
                // Just treat all properties as changed, there is no other choice.
3461 27
                $this->propertyChanged($managedCopy, $name, null, $prop->getValue($managedCopy));
3462
            }
3463
        }
3464 27
    }
3465
3466
    /**
3467
     * This method called by hydrators, and indicates that hydrator totally completed current hydration cycle.
3468
     * Unit of work able to fire deferred events, related to loading events here.
3469
     *
3470
     * @internal should be called internally from object hydrators
3471
     */
3472 859
    public function hydrationComplete()
3473
    {
3474 859
        $this->hydrationCompleteHandler->hydrationComplete();
3475 859
    }
3476
}
3477