Failed Conditions
Pull Request — 2.7 (#7950)
by Mathieu
07:30
created

ObjectHydrator::hydrateAllData()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 4
nop 0
dl 0
loc 14
ccs 7
cts 7
cp 1
crap 3
rs 10
c 0
b 0
f 0
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
use function method_exists;
30
use function spl_object_hash;
31
32
/**
33
 * The ObjectHydrator constructs an object graph out of an SQL result set.
34
 *
35
 * Internal note: Highly performance-sensitive code.
36
 *
37
 * @since  2.0
38
 * @author Roman Borschel <[email protected]>
39
 * @author Guilherme Blanco <[email protected]>
40
 * @author Fabio B. Silva <[email protected]>
41
 */
42
class ObjectHydrator extends AbstractHydrator
43
{
44
    /**
45
     * @var array
46
     */
47
    private $identifierMap = [];
48
49
    /**
50
     * @var array
51
     */
52
    private $resultPointers = [];
53
54
    /**
55
     * @var array
56
     */
57
    private $idTemplate = [];
58
59
    /**
60
     * @var integer
61
     */
62
    private $resultCounter = 0;
63
64
    /**
65
     * @var array
66
     */
67
    private $rootAliases = [];
68
69
    /**
70
     * @var array
71
     */
72
    private $initializedCollections = [];
73
74
    /**
75
     * @var array
76
     */
77
    private $existingCollections = [];
78
79
    /**
80
     * {@inheritdoc}
81
     */
82 710
    protected function prepare()
83
    {
84 710
        if ( ! isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) {
85 607
            $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] = true;
86
        }
87
88 710
        foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
89 675
            $this->identifierMap[$dqlAlias] = [];
90 675
            $this->idTemplate[$dqlAlias]    = '';
91
92
            // Remember which associations are "fetch joined", so that we know where to inject
93
            // collection stubs or proxies and where not.
94 675
            if ( ! isset($this->_rsm->relationMap[$dqlAlias])) {
95 675
                continue;
96
            }
97
98 331
            $parent = $this->_rsm->parentAliasMap[$dqlAlias];
99
100 331
            if ( ! isset($this->_rsm->aliasMap[$parent])) {
101 1
                throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $parent);
102
            }
103
104 330
            $sourceClassName = $this->_rsm->aliasMap[$parent];
105 330
            $sourceClass     = $this->getClassMetadata($sourceClassName);
106 330
            $assoc           = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
107
108 330
            $this->_hints['fetched'][$parent][$assoc['fieldName']] = true;
109
110 330
            if ($assoc['type'] === ClassMetadata::MANY_TO_MANY) {
111 35
                continue;
112
            }
113
114
            // Mark any non-collection opposite sides as fetched, too.
115 309
            if ($assoc['mappedBy']) {
116 260
                $this->_hints['fetched'][$dqlAlias][$assoc['mappedBy']] = true;
117
118 260
                continue;
119
            }
120
121
            // handle fetch-joined owning side bi-directional one-to-one associations
122 68
            if ($assoc['inversedBy']) {
123 47
                $class        = $this->getClassMetadata($className);
124 47
                $inverseAssoc = $class->associationMappings[$assoc['inversedBy']];
125
126 47
                if ( ! ($inverseAssoc['type'] & ClassMetadata::TO_ONE)) {
127 28
                    continue;
128
                }
129
130 41
                $this->_hints['fetched'][$dqlAlias][$inverseAssoc['fieldName']] = true;
131
            }
132
        }
133 709
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138 704
    protected function cleanup()
139
    {
140 704
        $eagerLoad = (isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) && $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] == true;
141
142 704
        parent::cleanup();
143
144 704
        $this->identifierMap =
145 704
        $this->initializedCollections =
146 704
        $this->existingCollections =
147 704
        $this->resultPointers = [];
148
149 704
        if ($eagerLoad) {
150 704
            $this->_uow->triggerEagerLoads();
151
        }
152
153 704
        $this->_uow->hydrationComplete();
154 704
    }
155
156
    /**
157
     * {@inheritdoc}
158
     */
159 703
    protected function hydrateAllData()
160
    {
161 703
        $result = [];
162
163 703
        while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
164 655
            $this->hydrateRowData($row, $result);
165
        }
166
167
        // Take snapshots from all newly initialized collections
168 700
        foreach ($this->initializedCollections as $coll) {
169 71
            $coll->takeSnapshot();
170
        }
171
172 700
        return $result;
173
    }
174
175
    /**
176
     * Initializes a related collection.
177
     *
178
     * @param object        $entity         The entity to which the collection belongs.
179
     * @param ClassMetadata $class
180
     * @param string        $fieldName      The name of the field on the entity that holds the collection.
181
     * @param string        $parentDqlAlias Alias of the parent fetch joining this collection.
182
     *
183
     * @return \Doctrine\ORM\PersistentCollection
184
     */
185 103
    private function initRelatedCollection($entity, $class, $fieldName, $parentDqlAlias)
186
    {
187 103
        $oid       = spl_object_hash($entity);
188 103
        $relation  = $class->getAssociationMapping($fieldName);
189 103
        $reflField = $class->getReflectionProperty($fieldName);
190 103
        $value     = null;
191
192 103
        if (! method_exists($reflField, 'isInitialized') || $reflField->isInitialized($entity)) {
193 103
            $value = $reflField->getValue($entity);
194
        }
195
196 103
        if ($value === null || is_array($value)) {
197 67
            $value = new ArrayCollection((array) $value);
198
        }
199
200 103
        if ( ! $value instanceof PersistentCollection) {
201 67
            $value = new PersistentCollection(
202 67
                $this->_em, $this->_metadataCache[$relation['targetEntity']], $value
203
            );
204 67
            $value->setOwner($entity, $relation);
205
206 67
            $reflField->setValue($entity, $value);
207 67
            $this->_uow->setOriginalEntityProperty($oid, $fieldName, $value);
208
209 67
            $this->initializedCollections[$oid . $fieldName] = $value;
210
        } else if (
211 40
            isset($this->_hints[Query::HINT_REFRESH]) ||
212 40
            isset($this->_hints['fetched'][$parentDqlAlias][$fieldName]) &&
213 40
             ! $value->isInitialized()
214
        ) {
215
            // Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED!
216 6
            $value->setDirty(false);
217 6
            $value->setInitialized(true);
218 6
            $value->unwrap()->clear();
219
220 6
            $this->initializedCollections[$oid . $fieldName] = $value;
221
        } else {
222
            // Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN!
223 35
            $this->existingCollections[$oid . $fieldName] = $value;
224
        }
225
226 103
        return $value;
227
    }
228
229
    /**
230
     * Gets an entity instance.
231
     *
232
     * @param array  $data     The instance data.
233
     * @param string $dqlAlias The DQL alias of the entity's class.
234
     *
235
     * @return object The entity.
236
     *
237
     * @throws HydrationException
238
     */
239 624
    private function getEntity(array $data, $dqlAlias)
240
    {
241 624
        $className = $this->_rsm->aliasMap[$dqlAlias];
242
243 624
        if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) {
244 70
            $fieldName = $this->_rsm->discriminatorColumns[$dqlAlias];
245
246 70
            if ( ! isset($this->_rsm->metaMappings[$fieldName])) {
247 1
                throw HydrationException::missingDiscriminatorMetaMappingColumn($className, $fieldName, $dqlAlias);
248
            }
249
250 69
            $discrColumn = $this->_rsm->metaMappings[$fieldName];
251
252 69
            if ( ! isset($data[$discrColumn])) {
253 1
                throw HydrationException::missingDiscriminatorColumn($className, $discrColumn, $dqlAlias);
254
            }
255
256 69
            if ($data[$discrColumn] === "") {
257
                throw HydrationException::emptyDiscriminatorValue($dqlAlias);
258
            }
259
260 69
            $discrMap = $this->_metadataCache[$className]->discriminatorMap;
261 69
            $discriminatorValue = (string) $data[$discrColumn];
262
263 69
            if ( ! isset($discrMap[$discriminatorValue])) {
264 1
                throw HydrationException::invalidDiscriminatorValue($discriminatorValue, array_keys($discrMap));
265
            }
266
267 68
            $className = $discrMap[$discriminatorValue];
268
269 68
            unset($data[$discrColumn]);
270
        }
271
272 622
        if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->rootAliases[$dqlAlias])) {
273 25
            $this->registerManaged($this->_metadataCache[$className], $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
274
        }
275
276 622
        $this->_hints['fetchAlias'] = $dqlAlias;
277
278 622
        return $this->_uow->createEntity($className, $data, $this->_hints);
279
    }
280
281
    /**
282
     * @param string $className
283
     * @param array  $data
284
     *
285
     * @return mixed
286
     */
287 35
    private function getEntityFromIdentityMap($className, array $data)
288
    {
289
        // TODO: Abstract this code and UnitOfWork::createEntity() equivalent?
290 35
        $class = $this->_metadataCache[$className];
291
292
        /* @var $class ClassMetadata */
293 35
        if ($class->isIdentifierComposite) {
294 1
            $idHash = '';
295
296 1
            foreach ($class->identifier as $fieldName) {
297 1
                $idHash .= ' ' . (isset($class->associationMappings[$fieldName])
298 1
                    ? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']]
299 1
                    : $data[$fieldName]);
300
            }
301
302 1
            return $this->_uow->tryGetByIdHash(ltrim($idHash), $class->rootEntityName);
303 34
        } else if (isset($class->associationMappings[$class->identifier[0]])) {
304
            return $this->_uow->tryGetByIdHash($data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']], $class->rootEntityName);
305
        }
306
307 34
        return $this->_uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName);
308
    }
309
310
    /**
311
     * Hydrates a single row in an SQL result set.
312
     *
313
     * @internal
314
     * First, the data of the row is split into chunks where each chunk contains data
315
     * that belongs to a particular component/class. Afterwards, all these chunks
316
     * are processed, one after the other. For each chunk of class data only one of the
317
     * following code paths is executed:
318
     *
319
     * Path A: The data chunk belongs to a joined/associated object and the association
320
     *         is collection-valued.
321
     * Path B: The data chunk belongs to a joined/associated object and the association
322
     *         is single-valued.
323
     * Path C: The data chunk belongs to a root result element/object that appears in the topmost
324
     *         level of the hydrated result. A typical example are the objects of the type
325
     *         specified by the FROM clause in a DQL query.
326
     *
327
     * @param array $row    The data of the row to process.
328
     * @param array $result The result array to fill.
329
     *
330
     * @return void
331
     */
332 660
    protected function hydrateRowData(array $row, array &$result)
333
    {
334
        // Initialize
335 660
        $id = $this->idTemplate; // initialize the id-memory
336 660
        $nonemptyComponents = [];
337
        // Split the row data into chunks of class data.
338 660
        $rowData = $this->gatherRowData($row, $id, $nonemptyComponents);
339
340
        // reset result pointers for each data row
341 660
        $this->resultPointers = [];
342
343
        // Hydrate the data chunks
344 660
        foreach ($rowData['data'] as $dqlAlias => $data) {
345 624
            $entityName = $this->_rsm->aliasMap[$dqlAlias];
346
347 624
            if (isset($this->_rsm->parentAliasMap[$dqlAlias])) {
348
                // It's a joined result
349
350 322
                $parentAlias = $this->_rsm->parentAliasMap[$dqlAlias];
351
                // we need the $path to save into the identifier map which entities were already
352
                // seen for this parent-child relationship
353 322
                $path = $parentAlias . '.' . $dqlAlias;
354
355
                // We have a RIGHT JOIN result here. Doctrine cannot hydrate RIGHT JOIN Object-Graphs
356 322
                if ( ! isset($nonemptyComponents[$parentAlias])) {
357
                    // TODO: Add special case code where we hydrate the right join objects into identity map at least
358 2
                    continue;
359
                }
360
361 322
                $parentClass    = $this->_metadataCache[$this->_rsm->aliasMap[$parentAlias]];
362 322
                assert($parentClass instanceof ClassMetadata);
363 322
                $relationField  = $this->_rsm->relationMap[$dqlAlias];
364 322
                $relation       = $parentClass->getAssociationMapping($relationField);
365 322
                $reflField      = $parentClass->getReflectionProperty($relationField);
366
367
                // Get a reference to the parent object to which the joined element belongs.
368 322
                if ($this->_rsm->isMixed && isset($this->rootAliases[$parentAlias])) {
369 18
                    $objectClass = $this->resultPointers[$parentAlias];
370 18
                    $parentObject = $objectClass[key($objectClass)];
371 306
                } else if (isset($this->resultPointers[$parentAlias])) {
372 306
                    $parentObject = $this->resultPointers[$parentAlias];
373
                } else {
374
                    // Parent object of relation not found, mark as not-fetched again
375 2
                    $element = $this->getEntity($data, $dqlAlias);
376
377
                    // Update result pointer and provide initial fetch data for parent
378 2
                    $this->resultPointers[$dqlAlias] = $element;
379 2
                    $rowData['data'][$parentAlias][$relationField] = $element;
380
381
                    // Mark as not-fetched again
382 2
                    unset($this->_hints['fetched'][$parentAlias][$relationField]);
383 2
                    continue;
384
                }
385
386 322
                $oid            = spl_object_hash($parentObject);
387 322
                $reflFieldValue = null;
388
389 322
                if (! method_exists($reflField, 'isInitialized') || $reflField->isInitialized($parentObject)) {
390 322
                    $reflFieldValue = $reflField->getValue($parentObject);
391
                }
392
393
                // Check the type of the relation (many or single-valued)
394 322
                if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
395
                    // PATH A: Collection-valued association
396 104
                    if (isset($nonemptyComponents[$dqlAlias])) {
397 100
                        $collKey = $oid . $relationField;
398 100
                        if (isset($this->initializedCollections[$collKey])) {
399 47
                            $reflFieldValue = $this->initializedCollections[$collKey];
400 100
                        } else if ( ! isset($this->existingCollections[$collKey])) {
401 100
                            $reflFieldValue = $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);
402
                        }
403
404 100
                        $indexExists    = isset($this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]);
405 100
                        $index          = $indexExists ? $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false;
406 100
                        $indexIsValid   = $index !== false ? isset($reflFieldValue[$index]) : false;
407
408 100
                        if ( ! $indexExists || ! $indexIsValid) {
409 100
                            if (isset($this->existingCollections[$collKey])) {
410
                                // Collection exists, only look for the element in the identity map.
411 35
                                if ($element = $this->getEntityFromIdentityMap($entityName, $data)) {
412 35
                                    $this->resultPointers[$dqlAlias] = $element;
413
                                } else {
414 35
                                    unset($this->resultPointers[$dqlAlias]);
415
                                }
416
                            } else {
417 68
                                $element = $this->getEntity($data, $dqlAlias);
418
419 68
                                if (isset($this->_rsm->indexByMap[$dqlAlias])) {
420 12
                                    $indexValue = $row[$this->_rsm->indexByMap[$dqlAlias]];
421 12
                                    $reflFieldValue->hydrateSet($indexValue, $element);
422 12
                                    $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue;
423
                                } else {
424 56
                                    $reflFieldValue->hydrateAdd($element);
425 56
                                    $reflFieldValue->last();
426 56
                                    $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key();
427
                                }
428
                                // Update result pointer
429 100
                                $this->resultPointers[$dqlAlias] = $element;
430
                            }
431
                        } else {
432
                            // Update result pointer
433 100
                            $this->resultPointers[$dqlAlias] = $reflFieldValue[$index];
434
                        }
435 9
                    } else if ( ! $reflFieldValue) {
436 5
                        $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);
437 6
                    } else if ($reflFieldValue instanceof PersistentCollection && $reflFieldValue->isInitialized() === false) {
438 104
                        $reflFieldValue->setInitialized(true);
439
                    }
440
441
                } else {
442
                    // PATH B: Single-valued association
443 240
                    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?
Loading history...
444
                        // we only need to take action if this value is null,
445
                        // we refresh the entity or its an uninitialized proxy.
446 225
                        if (isset($nonemptyComponents[$dqlAlias])) {
447 120
                            $element = $this->getEntity($data, $dqlAlias);
448 119
                            $reflField->setValue($parentObject, $element);
449 119
                            $this->_uow->setOriginalEntityProperty($oid, $relationField, $element);
450 119
                            $targetClass = $this->_metadataCache[$relation['targetEntity']];
451 119
                            assert($targetClass instanceof ClassMetadata);
452
453 119
                            if ($relation['isOwningSide']) {
454
                                // TODO: Just check hints['fetched'] here?
455
                                // If there is an inverse mapping on the target class its bidirectional
456 53
                                if ($relation['inversedBy']) {
457 36
                                    $inverseAssoc = $targetClass->getAssociationMapping($relation['inversedBy']);
458 36
                                    if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) {
459 13
                                        $targetClass->getReflectionProperty($inverseAssoc['fieldName'])->setValue($element, $parentObject);
460 36
                                        $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc['fieldName'], $parentObject);
461
                                    }
462 18
                                } else if ($parentClass === $targetClass && $relation['mappedBy']) {
463
                                    // Special case: bi-directional self-referencing one-one on the same class
464 53
                                    $targetClass->getReflectionProperty($relationField)->setValue($element, $parentObject);
465
                                }
466
                            } else {
467
                                // For sure bidirectional, as there is no inverse side in unidirectional mappings
468 69
                                $targetClass->getReflectionProperty($relation['mappedBy'])->setValue($element, $parentObject);
469 69
                                $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $relation['mappedBy'], $parentObject);
470
                            }
471
                            // Update result pointer
472 119
                            $this->resultPointers[$dqlAlias] = $element;
473
                        } else {
474 125
                            $this->_uow->setOriginalEntityProperty($oid, $relationField, null);
475 224
                            $reflField->setValue($parentObject, null);
476
                        }
477
                        // else leave $reflFieldValue null for single-valued associations
478
                    } else {
479
                        // Update result pointer
480 321
                        $this->resultPointers[$dqlAlias] = $reflFieldValue;
481
                    }
482
                }
483
            } else {
484
                // PATH C: Its a root result element
485 624
                $this->rootAliases[$dqlAlias] = true; // Mark as root alias
486 624
                $entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0;
487
488
                // if this row has a NULL value for the root result id then make it a null result.
489 624
                if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
490 3
                    if ($this->_rsm->isMixed) {
491 2
                        $result[] = [$entityKey => null];
492
                    } else {
493 1
                        $result[] = null;
494
                    }
495 3
                    $resultKey = $this->resultCounter;
496 3
                    ++$this->resultCounter;
497 3
                    continue;
498
                }
499
500
                // check for existing result from the iterations before
501 624
                if ( ! isset($this->identifierMap[$dqlAlias][$id[$dqlAlias]])) {
502 624
                    $element = $this->getEntity($data, $dqlAlias);
503
504 622
                    if ($this->_rsm->isMixed) {
505 41
                        $element = [$entityKey => $element];
506
                    }
507
508 622
                    if (isset($this->_rsm->indexByMap[$dqlAlias])) {
509 27
                        $resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]];
510
511 27
                        if (isset($this->_hints['collection'])) {
512 10
                            $this->_hints['collection']->hydrateSet($resultKey, $element);
513
                        }
514
515 27
                        $result[$resultKey] = $element;
516
                    } else {
517 605
                        $resultKey = $this->resultCounter;
518 605
                        ++$this->resultCounter;
519
520 605
                        if (isset($this->_hints['collection'])) {
521 112
                            $this->_hints['collection']->hydrateAdd($element);
522
                        }
523
524 605
                        $result[] = $element;
525
                    }
526
527 622
                    $this->identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey;
528
529
                    // Update result pointer
530 622
                    $this->resultPointers[$dqlAlias] = $element;
531
532
                } else {
533
                    // Update result pointer
534 78
                    $index = $this->identifierMap[$dqlAlias][$id[$dqlAlias]];
535 78
                    $this->resultPointers[$dqlAlias] = $result[$index];
536 78
                    $resultKey = $index;
537
                }
538
            }
539
540 622
            if (isset($this->_hints[Query::HINT_INTERNAL_ITERATION]) && $this->_hints[Query::HINT_INTERNAL_ITERATION]) {
541 622
                $this->_uow->hydrationComplete();
542
            }
543
        }
544
545 657
        if ( ! isset($resultKey) ) {
546 36
            $this->resultCounter++;
547
        }
548
549
        // Append scalar values to mixed result sets
550 657
        if (isset($rowData['scalars'])) {
551 55
            if ( ! isset($resultKey) ) {
552 23
                $resultKey = (isset($this->_rsm->indexByMap['scalars']))
553 2
                    ? $row[$this->_rsm->indexByMap['scalars']]
554 23
                    : $this->resultCounter - 1;
555
            }
556
557 55
            foreach ($rowData['scalars'] as $name => $value) {
558 55
                $result[$resultKey][$name] = $value;
559
            }
560
        }
561
562
        // Append new object to mixed result sets
563 657
        if (isset($rowData['newObjects'])) {
564 19
            if ( ! isset($resultKey) ) {
565 13
                $resultKey = $this->resultCounter - 1;
566
            }
567
568
569 19
            $scalarCount = (isset($rowData['scalars'])? count($rowData['scalars']): 0);
570
571 19
            foreach ($rowData['newObjects'] as $objIndex => $newObject) {
572 19
                $class  = $newObject['class'];
573 19
                $args   = $newObject['args'];
574 19
                $obj    = $class->newInstanceArgs($args);
575
576 19
                if ($scalarCount == 0 && count($rowData['newObjects']) == 1 ) {
577 10
                    $result[$resultKey] = $obj;
578
579 10
                    continue;
580
                }
581
582 9
                $result[$resultKey][$objIndex] = $obj;
583
            }
584
        }
585 657
    }
586
587
    /**
588
     * When executed in a hydrate() loop we may have to clear internal state to
589
     * decrease memory consumption.
590
     *
591
     * @param mixed $eventArgs
592
     *
593
     * @return void
594
     */
595 4
    public function onClear($eventArgs)
596
    {
597 4
        parent::onClear($eventArgs);
598
599 4
        $aliases             = array_keys($this->identifierMap);
600
601 4
        $this->identifierMap = array_fill_keys($aliases, []);
602 4
    }
603
}
604