vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php line 271

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