Completed
Pull Request — master (#6055)
by Martin
09:59
created

ObjectHydrator::hydrateAllData()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

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