Failed Conditions
Pull Request — master (#6546)
by Jáchym
11:13
created

ObjectHydrator::onClear()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
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\Internal\Hydration;
21
22
use Doctrine\ORM\UnitOfWork;
23
use PDO;
24
use Doctrine\ORM\Mapping\ClassMetadata;
25
use Doctrine\ORM\PersistentCollection;
26
use Doctrine\ORM\Query;
27
use Doctrine\Common\Collections\ArrayCollection;
28
use Doctrine\ORM\Proxy\Proxy;
29
30
/**
31
 * The ObjectHydrator constructs an object graph out of an SQL result set.
32
 *
33
 * Internal note: Highly performance-sensitive code.
34
 *
35
 * @since  2.0
36
 * @author Roman Borschel <[email protected]>
37
 * @author Guilherme Blanco <[email protected]>
38
 * @author Fabio B. Silva <[email protected]>
39
 */
40
class ObjectHydrator extends AbstractHydrator
41
{
42
    /**
43
     * @var array
44
     */
45
    private $identifierMap = [];
46
47
    /**
48
     * @var array
49
     */
50
    private $resultPointers = [];
51
52
    /**
53
     * @var array
54
     */
55
    private $idTemplate = [];
56
57
    /**
58
     * @var integer
59
     */
60
    private $resultCounter = 0;
61
62
    /**
63
     * @var array
64
     */
65
    private $rootAliases = [];
66
67
    /**
68
     * @var array
69
     */
70
    private $initializedCollections = [];
71
72
    /**
73
     * @var array
74
     */
75
    private $existingCollections = [];
76
77
    /**
78
     * {@inheritdoc}
79
     */
80 673
    protected function prepare()
81
    {
82 673
        if ( ! isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) {
83 573
            $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] = true;
84
        }
85
86 673
        foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
87 639
            $this->identifierMap[$dqlAlias] = [];
88 639
            $this->idTemplate[$dqlAlias]    = '';
89
90
            // Remember which associations are "fetch joined", so that we know where to inject
91
            // collection stubs or proxies and where not.
92 639
            if ( ! isset($this->_rsm->relationMap[$dqlAlias])) {
93 639
                continue;
94
            }
95
96 326
            $parent = $this->_rsm->parentAliasMap[$dqlAlias];
97
98 326
            if ( ! isset($this->_rsm->aliasMap[$parent])) {
99 1
                throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $parent);
100
            }
101
102 325
            $sourceClassName = $this->_rsm->aliasMap[$parent];
103 325
            $sourceClass     = $this->getClassMetadata($sourceClassName);
104 325
            $assoc           = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
105
106 325
            $this->_hints['fetched'][$parent][$assoc['fieldName']] = true;
107
108 325
            if ($assoc['type'] === ClassMetadata::MANY_TO_MANY) {
109 35
                continue;
110
            }
111
112
            // Mark any non-collection opposite sides as fetched, too.
113 304
            if ($assoc['mappedBy']) {
114 256
                $this->_hints['fetched'][$dqlAlias][$assoc['mappedBy']] = true;
115
116 256
                continue;
117
            }
118
119
            // handle fetch-joined owning side bi-directional one-to-one associations
120 66
            if ($assoc['inversedBy']) {
121 46
                $class        = $this->getClassMetadata($className);
122 46
                $inverseAssoc = $class->associationMappings[$assoc['inversedBy']];
123
124 46
                if ( ! ($inverseAssoc['type'] & ClassMetadata::TO_ONE)) {
125 28
                    continue;
126
                }
127
128 39
                $this->_hints['fetched'][$dqlAlias][$inverseAssoc['fieldName']] = true;
129
            }
130
        }
131 672
    }
132
133
    /**
134
     * {@inheritdoc}
135
     */
136 667
    protected function cleanup()
137
    {
138 667
        $eagerLoad = (isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) && $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] == true;
139
140 667
        parent::cleanup();
141
142 667
        $this->identifierMap =
143 667
        $this->initializedCollections =
144 667
        $this->existingCollections =
145 667
        $this->resultPointers = [];
146
147 667
        if ($eagerLoad) {
148 667
            $this->_uow->triggerEagerLoads();
149
        }
150
151 667
        $this->_uow->hydrationComplete();
152 667
    }
153
154
    /**
155
     * {@inheritdoc}
156
     */
157 666 View Code Duplication
    protected function hydrateAllData()
158
    {
159 666
        $result = [];
160
161 666
        while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
162 628
            $this->hydrateRowData($row, $result);
163
        }
164
165
        // Take snapshots from all newly initialized collections
166 663
        foreach ($this->initializedCollections as $coll) {
167 69
            $coll->takeSnapshot();
168
        }
169
170 663
        return $result;
171
    }
172
173
    /**
174
     * Initializes a related collection.
175
     *
176
     * @param object        $entity         The entity to which the collection belongs.
177
     * @param ClassMetadata $class
178
     * @param string        $fieldName      The name of the field on the entity that holds the collection.
179
     * @param string        $parentDqlAlias Alias of the parent fetch joining this collection.
180
     *
181
     * @return \Doctrine\ORM\PersistentCollection
182
     */
183 101
    private function initRelatedCollection($entity, $class, $fieldName, $parentDqlAlias)
184
    {
185 101
        $oid      = spl_object_hash($entity);
186 101
        $relation = $class->associationMappings[$fieldName];
187 101
        $value    = $class->reflFields[$fieldName]->getValue($entity);
188
189 101
        if ($value === null || is_array($value)) {
190 65
            $value = new ArrayCollection((array) $value);
191
        }
192
193 101
        if ( ! $value instanceof PersistentCollection) {
194 65
            $value = new PersistentCollection(
195 65
                $this->_em, $this->_metadataCache[$relation['targetEntity']], $value
196
            );
197 65
            $value->setOwner($entity, $relation);
198
199 65
            $class->reflFields[$fieldName]->setValue($entity, $value);
200 65
            $this->_uow->setOriginalEntityProperty($oid, $fieldName, $value);
201
202 65
            $this->initializedCollections[$oid . $fieldName] = $value;
203
        } else if (
204 39
            isset($this->_hints[Query::HINT_REFRESH]) ||
205 39
            isset($this->_hints['fetched'][$parentDqlAlias][$fieldName]) &&
206 39
             ! $value->isInitialized()
207
        ) {
208
            // Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED!
209 6
            $value->setDirty(false);
210 6
            $value->setInitialized(true);
211 6
            $value->unwrap()->clear();
212
213 6
            $this->initializedCollections[$oid . $fieldName] = $value;
214
        } else {
215
            // Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN!
216 34
            $this->existingCollections[$oid . $fieldName] = $value;
217
        }
218
219 101
        return $value;
220
    }
221
222
    /**
223
     * Gets an entity instance.
224
     *
225
     * @param array  $data     The instance data.
226
     * @param string $dqlAlias The DQL alias of the entity's class.
227
     *
228
     * @return object The entity.
229
     *
230
     * @throws HydrationException
231
     */
232 598
    private function getEntity(array $data, $dqlAlias)
233
    {
234 598
        $className = $this->_rsm->aliasMap[$dqlAlias];
235
236 598
        if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) {
237 62
            $fieldName = $this->_rsm->discriminatorColumns[$dqlAlias];
238
239 62
            if ( ! isset($this->_rsm->metaMappings[$fieldName])) {
240 1
                throw HydrationException::missingDiscriminatorMetaMappingColumn($className, $fieldName, $dqlAlias);
241
            }
242
243 61
            $discrColumn = $this->_rsm->metaMappings[$fieldName];
244
245 61
            if ( ! isset($data[$discrColumn])) {
246 1
                throw HydrationException::missingDiscriminatorColumn($className, $discrColumn, $dqlAlias);
247
            }
248
249 61
            if ($data[$discrColumn] === "") {
250
                throw HydrationException::emptyDiscriminatorValue($dqlAlias);
251
            }
252
253 61
            $discrMap = $this->_metadataCache[$className]->discriminatorMap;
254 61
            $discriminatorValue = (string) $data[$discrColumn];
255
256 61
            if ( ! isset($discrMap[$discriminatorValue])) {
257 1
                throw HydrationException::invalidDiscriminatorValue($discriminatorValue, array_keys($discrMap));
258
            }
259
260 60
            $className = $discrMap[$discriminatorValue];
261
262 60
            unset($data[$discrColumn]);
263
        }
264
265 596
        if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->rootAliases[$dqlAlias])) {
266 24
            $this->registerManaged($this->_metadataCache[$className], $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
267
        }
268
269 596
        $this->_hints['fetchAlias'] = $dqlAlias;
270
271 596
        return $this->_uow->createEntity($className, $data, $this->_hints);
272
    }
273
274
    /**
275
     * @param string $className
276
     * @param array  $data
277
     *
278
     * @return mixed
279
     */
280 34
    private function getEntityFromIdentityMap($className, array $data)
281
    {
282
        // TODO: Abstract this code and UnitOfWork::createEntity() equivalent?
283 34
        $class = $this->_metadataCache[$className];
284
285
        /* @var $class ClassMetadata */
286 34
        if ($class->isIdentifierComposite) {
287 1
            $idHash = '';
288
289 1 View Code Duplication
            foreach ($class->identifier as $fieldName) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
290 1
                $idHash .= ' ' . (isset($class->associationMappings[$fieldName])
291 1
                    ? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']]
292 1
                    : $data[$fieldName]);
293
            }
294
295 1
            return $this->_uow->tryGetByIdHash(ltrim($idHash), $class->rootEntityName);
296 33
        } else if (isset($class->associationMappings[$class->identifier[0]])) {
297
            return $this->_uow->tryGetByIdHash($data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']], $class->rootEntityName);
298
        }
299
300 33
        return $this->_uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName);
301
    }
302
303
    /**
304
     * Hydrates a single row in an SQL result set.
305
     *
306
     * @internal
307
     * First, the data of the row is split into chunks where each chunk contains data
308
     * that belongs to a particular component/class. Afterwards, all these chunks
309
     * are processed, one after the other. For each chunk of class data only one of the
310
     * following code paths is executed:
311
     *
312
     * Path A: The data chunk belongs to a joined/associated object and the association
313
     *         is collection-valued.
314
     * Path B: The data chunk belongs to a joined/associated object and the association
315
     *         is single-valued.
316
     * Path C: The data chunk belongs to a root result element/object that appears in the topmost
317
     *         level of the hydrated result. A typical example are the objects of the type
318
     *         specified by the FROM clause in a DQL query.
319
     *
320
     * @param array $row    The data of the row to process.
321
     * @param array $result The result array to fill.
322
     *
323
     * @return void
324
     */
325 633
    protected function hydrateRowData(array $row, array &$result)
326
    {
327
        // Initialize
328 633
        $id = $this->idTemplate; // initialize the id-memory
329 633
        $nonemptyComponents = [];
330
        // Split the row data into chunks of class data.
331 633
        $rowData = $this->gatherRowData($row, $id, $nonemptyComponents);
332
333
        // reset result pointers for each data row
334 633
        $this->resultPointers = [];
335
336
        // Hydrate the data chunks
337 633
        foreach ($rowData['data'] as $dqlAlias => $data) {
338 598
            $entityName = $this->_rsm->aliasMap[$dqlAlias];
339
340 598
            if (isset($this->_rsm->parentAliasMap[$dqlAlias])) {
341
                // It's a joined result
342
343 317
                $parentAlias = $this->_rsm->parentAliasMap[$dqlAlias];
344
                // we need the $path to save into the identifier map which entities were already
345
                // seen for this parent-child relationship
346 317
                $path = $parentAlias . '.' . $dqlAlias;
347
348
                // We have a RIGHT JOIN result here. Doctrine cannot hydrate RIGHT JOIN Object-Graphs
349 317
                if ( ! isset($nonemptyComponents[$parentAlias])) {
350
                    // TODO: Add special case code where we hydrate the right join objects into identity map at least
351 2
                    continue;
352
                }
353
354 317
                $parentClass    = $this->_metadataCache[$this->_rsm->aliasMap[$parentAlias]];
355 317
                $relationField  = $this->_rsm->relationMap[$dqlAlias];
356 317
                $relation       = $parentClass->associationMappings[$relationField];
357 317
                $reflField      = $parentClass->reflFields[$relationField];
358
359
                // Get a reference to the parent object to which the joined element belongs.
360 317
                if ($this->_rsm->isMixed && isset($this->rootAliases[$parentAlias])) {
361 18
                    $objectClass = $this->resultPointers[$parentAlias];
362 18
                    $parentObject = $objectClass[key($objectClass)];
363 301
                } else if (isset($this->resultPointers[$parentAlias])) {
364 301
                    $parentObject = $this->resultPointers[$parentAlias];
365
                } else {
366
                    // Parent object of relation not found, mark as not-fetched again
367 2
                    $element = $this->getEntity($data, $dqlAlias);
368
369
                    // Update result pointer and provide initial fetch data for parent
370 2
                    $this->resultPointers[$dqlAlias] = $element;
371 2
                    $rowData['data'][$parentAlias][$relationField] = $element;
372
373
                    // Mark as not-fetched again
374 2
                    unset($this->_hints['fetched'][$parentAlias][$relationField]);
375 2
                    continue;
376
                }
377
378 317
                $oid = spl_object_hash($parentObject);
379
380
                // Check the type of the relation (many or single-valued)
381 317
                if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
382
                    // PATH A: Collection-valued association
383 102
                    $reflFieldValue = $reflField->getValue($parentObject);
384
385 102
                    if (isset($nonemptyComponents[$dqlAlias])) {
386 98
                        $collKey = $oid . $relationField;
387 98
                        if (isset($this->initializedCollections[$collKey])) {
388 45
                            $reflFieldValue = $this->initializedCollections[$collKey];
389 98
                        } else if ( ! isset($this->existingCollections[$collKey])) {
390 98
                            $reflFieldValue = $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);
391
                        }
392
393 98
                        $indexExists    = isset($this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]);
394 98
                        $index          = $indexExists ? $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false;
395 98
                        $indexIsValid   = $index !== false ? isset($reflFieldValue[$index]) : false;
396
397 98
                        if ( ! $indexExists || ! $indexIsValid) {
398 98
                            if (isset($this->existingCollections[$collKey])) {
399
                                // Collection exists, only look for the element in the identity map.
400 34
                                if ($element = $this->getEntityFromIdentityMap($entityName, $data)) {
401 34
                                    $this->resultPointers[$dqlAlias] = $element;
402
                                } else {
403 34
                                    unset($this->resultPointers[$dqlAlias]);
404
                                }
405
                            } else {
406 66
                                $element = $this->getEntity($data, $dqlAlias);
407
408 66
                                if (isset($this->_rsm->indexByMap[$dqlAlias])) {
409 11
                                    $indexValue = $row[$this->_rsm->indexByMap[$dqlAlias]];
410 11
                                    $reflFieldValue->hydrateSet($indexValue, $element);
411 11
                                    $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue;
412
                                } else {
413 55
                                    $reflFieldValue->hydrateAdd($element);
414 55
                                    $reflFieldValue->last();
415 55
                                    $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key();
416
                                }
417
                                // Update result pointer
418 98
                                $this->resultPointers[$dqlAlias] = $element;
419
                            }
420
                        } else {
421
                            // Update result pointer
422 98
                            $this->resultPointers[$dqlAlias] = $reflFieldValue[$index];
423
                        }
424 9
                    } else if ( ! $reflFieldValue) {
425 5
                        $reflFieldValue = $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);
0 ignored issues
show
Unused Code introduced by
$reflFieldValue is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
426 6
                    } else if ($reflFieldValue instanceof PersistentCollection && $reflFieldValue->isInitialized() === false) {
427 102
                        $reflFieldValue->setInitialized(true);
428
                    }
429
430
                } else {
431
                    // PATH B: Single-valued association
432 236
                    $reflFieldValue = $reflField->getValue($parentObject);
433
434 236
                    if ( ! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH]) || ($reflFieldValue instanceof Proxy && !$reflFieldValue->__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...
435
                        // we only need to take action if this value is null,
436
                        // we refresh the entity or its an uninitialized proxy.
437 221
                        if (isset($nonemptyComponents[$dqlAlias])) {
438 116
                            $element = $this->getEntity($data, $dqlAlias);
439 115
                            $reflField->setValue($parentObject, $element);
440 115
                            $this->_uow->setOriginalEntityProperty($oid, $relationField, $element);
441 115
                            $targetClass = $this->_metadataCache[$relation['targetEntity']];
442
443 115
                            if ($relation['isOwningSide']) {
444
                                // TODO: Just check hints['fetched'] here?
445
                                // If there is an inverse mapping on the target class its bidirectional
446 51
                                if ($relation['inversedBy']) {
447 35
                                    $inverseAssoc = $targetClass->associationMappings[$relation['inversedBy']];
448 35 View Code Duplication
                                    if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
449 12
                                        $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($element, $parentObject);
450 35
                                        $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc['fieldName'], $parentObject);
451
                                    }
452 17
                                } else if ($parentClass === $targetClass && $relation['mappedBy']) {
453
                                    // Special case: bi-directional self-referencing one-one on the same class
454 51
                                    $targetClass->reflFields[$relationField]->setValue($element, $parentObject);
455
                                }
456 View Code Duplication
                            } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
457
                                // For sure bidirectional, as there is no inverse side in unidirectional mappings
458 67
                                $targetClass->reflFields[$relation['mappedBy']]->setValue($element, $parentObject);
459 67
                                $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $relation['mappedBy'], $parentObject);
460
                            }
461
                            // Update result pointer
462 115
                            $this->resultPointers[$dqlAlias] = $element;
463
                        } else {
464 125
                            $this->_uow->setOriginalEntityProperty($oid, $relationField, null);
465 220
                            $reflField->setValue($parentObject, null);
466
                        }
467
                        // else leave $reflFieldValue null for single-valued associations
468
                    } else {
469
                        // Update result pointer
470 316
                        $this->resultPointers[$dqlAlias] = $reflFieldValue;
471
                    }
472
                }
473
            } else {
474
                // PATH C: Its a root result element
475 598
                $this->rootAliases[$dqlAlias] = true; // Mark as root alias
476 598
                $entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0;
477
478
                // if this row has a NULL value for the root result id then make it a null result.
479 598 View Code Duplication
                if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
480 3
                    if ($this->_rsm->isMixed) {
481 2
                        $result[] = [$entityKey => null];
482
                    } else {
483 1
                        $result[] = null;
484
                    }
485 3
                    $resultKey = $this->resultCounter;
486 3
                    ++$this->resultCounter;
487 3
                    continue;
488
                }
489
490
                // check for existing result from the iterations before
491 598
                if ( ! isset($this->identifierMap[$dqlAlias][$id[$dqlAlias]])) {
492 598
                    $element = $this->getEntity($data, $dqlAlias);
493
494 596
                    if ($this->_rsm->isMixed) {
495 41
                        $element = [$entityKey => $element];
496
                    }
497
498 596
                    if (isset($this->_rsm->indexByMap[$dqlAlias])) {
499 26
                        $resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]];
500
501 26
                        if (isset($this->_hints['collection'])) {
502 10
                            $this->_hints['collection']->hydrateSet($resultKey, $element);
503
                        }
504
505 26
                        $result[$resultKey] = $element;
506
                    } else {
507 579
                        $resultKey = $this->resultCounter;
508 579
                        ++$this->resultCounter;
509
510 579
                        if (isset($this->_hints['collection'])) {
511 110
                            $this->_hints['collection']->hydrateAdd($element);
512
                        }
513
514 579
                        $result[] = $element;
515
                    }
516
517 596
                    $this->identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey;
518
519
                    // Update result pointer
520 596
                    $this->resultPointers[$dqlAlias] = $element;
521
522
                } else {
523
                    // Update result pointer
524 76
                    $index = $this->identifierMap[$dqlAlias][$id[$dqlAlias]];
525 76
                    $this->resultPointers[$dqlAlias] = $result[$index];
526 76
                    $resultKey = $index;
527
                }
528
            }
529
530 596 View Code Duplication
            if (isset($this->_hints[Query::HINT_INTERNAL_ITERATION]) && $this->_hints[Query::HINT_INTERNAL_ITERATION]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
531 596
                $this->_uow->hydrationComplete();
532
            }
533
        }
534
535 630
        if ( ! isset($resultKey) ) {
536 35
            $this->resultCounter++;
537
        }
538
539
        // Append scalar values to mixed result sets
540 630 View Code Duplication
        if (isset($rowData['scalars'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
541 54
            if ( ! isset($resultKey) ) {
542 22
                $resultKey = (isset($this->_rsm->indexByMap['scalars']))
543 2
                    ? $row[$this->_rsm->indexByMap['scalars']]
544 22
                    : $this->resultCounter - 1;
545
            }
546
547 54
            foreach ($rowData['scalars'] as $name => $value) {
548 54
                $result[$resultKey][$name] = $value;
549
            }
550
        }
551
552
        // Append new object to mixed result sets
553 630 View Code Duplication
        if (isset($rowData['newObjects'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
554 19
            if ( ! isset($resultKey) ) {
555 13
                $resultKey = $this->resultCounter - 1;
556
            }
557
558
559 19
            $scalarCount = (isset($rowData['scalars'])? count($rowData['scalars']): 0);
560
561 19
            foreach ($rowData['newObjects'] as $objIndex => $newObject) {
562 19
                $class  = $newObject['class'];
563 19
                $args   = $newObject['args'];
564 19
                $obj    = $class->newInstanceArgs($args);
565
566 19
                if ($scalarCount == 0 && count($rowData['newObjects']) == 1 ) {
567 10
                    $result[$resultKey] = $obj;
568
569 10
                    continue;
570
                }
571
572 9
                $result[$resultKey][$objIndex] = $obj;
573
            }
574
        }
575 630
    }
576
577
    /**
578
     * When executed in a hydrate() loop we may have to clear internal state to
579
     * decrease memory consumption.
580
     *
581
     * @param mixed $eventArgs
582
     *
583
     * @return void
584
     */
585 3
    public function onClear($eventArgs)
586
    {
587 3
        parent::onClear($eventArgs);
588
589 3
        $aliases             = array_keys($this->identifierMap);
590
591 3
        $this->identifierMap = array_fill_keys($aliases, []);
592 3
    }
593
}
594