text:
<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.doctrine-project.org>.
 */

namespace Doctrine\ORM\Internal\Hydration;

use PDO,
    Doctrine\ORM\Mapping\ClassMetadata,
    Doctrine\ORM\PersistentCollection,
    Doctrine\ORM\Query,
    Doctrine\Common\Collections\ArrayCollection,
    Doctrine\Common\Collections\Collection;

/**
 * The ObjectHydrator constructs an object graph out of an SQL result set.
 *
 * @author Roman Borschel <roman@code-factory.org>
 * @since 2.0
 * @internal Highly performance-sensitive code.
 */
class ObjectHydrator extends AbstractHydrator
{
    /* Local ClassMetadata cache to avoid going to the EntityManager all the time.
     * This local cache is maintained between hydration runs and not cleared.
     */
    private $_ce = array();

    /* The following parts are reinitialized on every hydration run. */

    private $_identifierMap;
    private $_resultPointers;
    private $_idTemplate;
    private $_resultCounter;
    private $_rootAliases = array();
    private $_initializedCollections = array();
    private $_existingCollections = array();
    //private $_createdEntities;

    /** @override */
    protected function _prepare()
    {
        $this->_identifierMap =
        $this->_resultPointers =
        $this->_idTemplate = array();
        $this->_resultCounter = 0;
        if (!isset($this->_hints['deferEagerLoad'])) {
            $this->_hints['deferEagerLoad'] = true;
        }

        foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
            $this->_identifierMap[$dqlAlias] = array();
            $this->_idTemplate[$dqlAlias] = '';
            $class = $this->_em->getClassMetadata($className);

            if ( ! isset($this->_ce[$className])) {
                $this->_ce[$className] = $class;
            }

            // Remember which associations are "fetch joined", so that we know where to inject
            // collection stubs or proxies and where not.
            if (isset($this->_rsm->relationMap[$dqlAlias])) {
                if ( ! isset($this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]])) {
                    throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $this->_rsm->parentAliasMap[$dqlAlias]);
                }

                $sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]];
                $sourceClass = $this->_getClassMetadata($sourceClassName);
                $assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
                $this->_hints['fetched'][$sourceClassName][$assoc['fieldName']] = true;
                if ($sourceClass->subClasses) {
                    foreach ($sourceClass->subClasses as $sourceSubclassName) {
                        $this->_hints['fetched'][$sourceSubclassName][$assoc['fieldName']] = true;
                    }
                }
                if ($assoc['type'] != ClassMetadata::MANY_TO_MANY) {
                    // Mark any non-collection opposite sides as fetched, too.
                    if ($assoc['mappedBy']) {
                        $this->_hints['fetched'][$className][$assoc['mappedBy']] = true;
                    } else {
                        if ($assoc['inversedBy']) {
                            $inverseAssoc = $class->associationMappings[$assoc['inversedBy']];
                            if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) {
                                $this->_hints['fetched'][$className][$inverseAssoc['fieldName']] = true;
                                if ($class->subClasses) {
                                    foreach ($class->subClasses as $targetSubclassName) {
                                        $this->_hints['fetched'][$targetSubclassName][$inverseAssoc['fieldName']] = true;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function _cleanup()
    {
        $eagerLoad = (isset($this->_hints['deferEagerLoad'])) && $this->_hints['deferEagerLoad'] == true;

        parent::_cleanup();
        $this->_identifierMap =
        $this->_initializedCollections =
        $this->_existingCollections =
        $this->_resultPointers = array();

        if ($eagerLoad) {
            $this->_em->getUnitOfWork()->triggerEagerLoads();
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function _hydrateAll()
    {
        $result = array();
        $cache = array();

        while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
            $this->_hydrateRow($row, $cache, $result);
        }

        // Take snapshots from all newly initialized collections
        foreach ($this->_initializedCollections as $coll) {
            $coll->takeSnapshot();
        }

        return $result;
    }

    /**
     * Initializes a related collection.
     *
     * @param object $entity The entity to which the collection belongs.
     * @param string $name The name of the field on the entity that holds the collection.
     */
    private function _initRelatedCollection($entity, $class, $fieldName)
    {
        $oid = spl_object_hash($entity);
        $relation = $class->associationMappings[$fieldName];

        $value = $class->reflFields[$fieldName]->getValue($entity);
        if ($value === null) {
            $value = new ArrayCollection;
        }

        if ( ! $value instanceof PersistentCollection) {
            $value = new PersistentCollection(
                $this->_em,
                $this->_ce[$relation['targetEntity']],
                $value
            );
            $value->setOwner($entity, $relation);
            $class->reflFields[$fieldName]->setValue($entity, $value);
            $this->_uow->setOriginalEntityProperty($oid, $fieldName, $value);
            $this->_initializedCollections[$oid . $fieldName] = $value;
        } else if (isset($this->_hints[Query::HINT_REFRESH]) ||
                isset($this->_hints['fetched'][$class->name][$fieldName]) &&
                ! $value->isInitialized()) {
            // Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED!
            $value->setDirty(false);
            $value->setInitialized(true);
            $value->unwrap()->clear();
            $this->_initializedCollections[$oid . $fieldName] = $value;
        } else {
            // Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN!
            $this->_existingCollections[$oid . $fieldName] = $value;
        }

        return $value;
    }

    /**
     * Gets an entity instance.
     *
     * @param $data The instance data.
     * @param $dqlAlias The DQL alias of the entity's class.
     * @return object The entity.
     */
    private function _getEntity(array $data, $dqlAlias)
    {
        $className = $this->_rsm->aliasMap[$dqlAlias];
        if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) {
            $discrColumn = $this->_rsm->metaMappings[$this->_rsm->discriminatorColumns[$dqlAlias]];
            $className = $this->_ce[$className]->discriminatorMap[$data[$discrColumn]];
            unset($data[$discrColumn]);
        }

        if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->_rootAliases[$dqlAlias])) {
            $class = $this->_ce[$className];
            $this->registerManaged($class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
        }

        return $this->_uow->createEntity($className, $data, $this->_hints);
    }

    private function _getEntityFromIdentityMap($className, array $data)
    {
        // TODO: Abstract this code and UnitOfWork::createEntity() equivalent?
        $class = $this->_ce[$className];
        /* @var $class ClassMetadata */
        if ($class->isIdentifierComposite) {
            $idHash = '';
            foreach ($class->identifier as $fieldName) {
                if (isset($class->associationMappings[$fieldName])) {
                    $idHash .= $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] . ' ';
                } else {
                    $idHash .= $data[$fieldName] . ' ';
                }
            }
            return $this->_uow->tryGetByIdHash(rtrim($idHash), $class->rootEntityName);
        } else if (isset($class->associationMappings[$class->identifier[0]])) {
            return $this->_uow->tryGetByIdHash($data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']], $class->rootEntityName);
        } else {
            return $this->_uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName);
        }
    }

    /**
     * Gets a ClassMetadata instance from the local cache.
     * If the instance is not yet in the local cache, it is loaded into the
     * local cache.
     *
     * @param string $className The name of the class.
     * @return ClassMetadata
     */
    private function _getClassMetadata($className)
    {
        if ( ! isset($this->_ce[$className])) {
            $this->_ce[$className] = $this->_em->getClassMetadata($className);
        }
        return $this->_ce[$className];
    }

    /**
     * Hydrates a single row in an SQL result set.
     *
     * @internal
     * First, the data of the row is split into chunks where each chunk contains data
     * that belongs to a particular component/class. Afterwards, all these chunks
     * are processed, one after the other. For each chunk of class data only one of the
     * following code paths is executed:
     *
     * Path A: The data chunk belongs to a joined/associated object and the association
     *         is collection-valued.
     * Path B: The data chunk belongs to a joined/associated object and the association
     *         is single-valued.
     * Path C: The data chunk belongs to a root result element/object that appears in the topmost
     *         level of the hydrated result. A typical example are the objects of the type
     *         specified by the FROM clause in a DQL query.
     *
     * @param array $data The data of the row to process.
     * @param array $cache The cache to use.
     * @param array $result The result array to fill.
     */
    protected function _hydrateRow(array $data, array &$cache, array &$result)
    {
        // Initialize
        $id = $this->_idTemplate; // initialize the id-memory
        $nonemptyComponents = array();
        // Split the row data into chunks of class data.
        $rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);

        // Extract scalar values. They're appended at the end.
        if (isset($rowData['scalars'])) {
            $scalars = $rowData['scalars'];
            unset($rowData['scalars']);
            if (empty($rowData)) {
                ++$this->_resultCounter;
            }
        }

        // Hydrate the data chunks
        foreach ($rowData as $dqlAlias => $data) {
            $entityName = $this->_rsm->aliasMap[$dqlAlias];

            if (isset($this->_rsm->parentAliasMap[$dqlAlias])) {
                // It's a joined result

                $parentAlias = $this->_rsm->parentAliasMap[$dqlAlias];
                // we need the $path to save into the identifier map which entities were already
                // seen for this parent-child relationship
                $path = $parentAlias . '.' . $dqlAlias;

                // Get a reference to the parent object to which the joined element belongs.
                if ($this->_rsm->isMixed && isset($this->_rootAliases[$parentAlias])) {
                    $first = reset($this->_resultPointers);
                    $parentObject = $this->_resultPointers[$parentAlias][key($first)];
                } else if (isset($this->_resultPointers[$parentAlias])) {
                    $parentObject = $this->_resultPointers[$parentAlias];
                } else {
                    // Parent object of relation not found, so skip it.
                    continue;
                }

                $parentClass = $this->_ce[$this->_rsm->aliasMap[$parentAlias]];
                $oid = spl_object_hash($parentObject);
                $relationField = $this->_rsm->relationMap[$dqlAlias];
                $relation = $parentClass->associationMappings[$relationField];
                $reflField = $parentClass->reflFields[$relationField];

                // Check the type of the relation (many or single-valued)
                if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
                    // PATH A: Collection-valued association
                    if (isset($nonemptyComponents[$dqlAlias])) {
                        $collKey = $oid . $relationField;
                        if (isset($this->_initializedCollections[$collKey])) {
                            $reflFieldValue = $this->_initializedCollections[$collKey];
                        } else if ( ! isset($this->_existingCollections[$collKey])) {
                            $reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField);
                        }

                        $indexExists = isset($this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]);
                        $index = $indexExists ? $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false;
                        $indexIsValid = $index !== false ? isset($reflFieldValue[$index]) : false;

                        if ( ! $indexExists || ! $indexIsValid) {
                            if (isset($this->_existingCollections[$collKey])) {
                                // Collection exists, only look for the element in the identity map.
                                if ($element = $this->_getEntityFromIdentityMap($entityName, $data)) {
                                    $this->_resultPointers[$dqlAlias] = $element;
                                } else {
                                    unset($this->_resultPointers[$dqlAlias]);
                                }
                            } else {
                                $element = $this->_getEntity($data, $dqlAlias);

                                if (isset($this->_rsm->indexByMap[$dqlAlias])) {
                                    $field = $this->_rsm->indexByMap[$dqlAlias];
                                    $indexValue = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
                                    $reflFieldValue->hydrateSet($indexValue, $element);
                                    $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue;
                                } else {
                                    $reflFieldValue->hydrateAdd($element);
                                    $reflFieldValue->last();
                                    $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key();
                                }
                                // Update result pointer
                                $this->_resultPointers[$dqlAlias] = $element;
                            }
                        } else {
                            // Update result pointer
                            $this->_resultPointers[$dqlAlias] = $reflFieldValue[$index];
                        }
                    } else if ( ! $reflField->getValue($parentObject)) {
                        $coll = new PersistentCollection($this->_em, $this->_ce[$entityName], new ArrayCollection);
                        $coll->setOwner($parentObject, $relation);
                        $reflField->setValue($parentObject, $coll);
                        $this->_uow->setOriginalEntityProperty($oid, $relationField, $coll);
                    }
                } else {
                    // PATH B: Single-valued association
                    $reflFieldValue = $reflField->getValue($parentObject);
                    if ( ! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH])) {
                        if (isset($nonemptyComponents[$dqlAlias])) {
                            $element = $this->_getEntity($data, $dqlAlias);
                            $reflField->setValue($parentObject, $element);
                            $this->_uow->setOriginalEntityProperty($oid, $relationField, $element);
                            $targetClass = $this->_ce[$relation['targetEntity']];
                            if ($relation['isOwningSide']) {
                                //TODO: Just check hints['fetched'] here?
                                // If there is an inverse mapping on the target class its bidirectional
                                if ($relation['inversedBy']) {
                                    $inverseAssoc = $targetClass->associationMappings[$relation['inversedBy']];
                                    if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) {
                                        $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($element, $parentObject);
                                        $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc['fieldName'], $parentObject);
                                    }
                                } else if ($parentClass === $targetClass && $relation['mappedBy']) {
                                    // Special case: bi-directional self-referencing one-one on the same class
                                    $targetClass->reflFields[$relationField]->setValue($element, $parentObject);
                                }
                            } else {
                                // For sure bidirectional, as there is no inverse side in unidirectional mappings
                                $targetClass->reflFields[$relation['mappedBy']]->setValue($element, $parentObject);
                                $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $relation['mappedBy'], $parentObject);
                            }
                            // Update result pointer
                            $this->_resultPointers[$dqlAlias] = $element;
                        }
                        // else leave $reflFieldValue null for single-valued associations
                    } else {
                        // Update result pointer
                        $this->_resultPointers[$dqlAlias] = $reflFieldValue;
                    }
                }
            } else {
                // PATH C: Its a root result element
                $this->_rootAliases[$dqlAlias] = true; // Mark as root alias

                if ( ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
                    $element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias);
                    if (isset($this->_rsm->indexByMap[$dqlAlias])) {
                        $field = $this->_rsm->indexByMap[$dqlAlias];
                        $key = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
                        if ($this->_rsm->isMixed) {
                            $element = array($key => $element);
                            $result[] = $element;
                            $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->_resultCounter;
                            ++$this->_resultCounter;
                        } else {
                            $result[$key] = $element;
                            $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $key;
                        }

                        if (isset($this->_hints['collection'])) {
                            $this->_hints['collection']->hydrateSet($key, $element);
                        }
                    } else {
                        if ($this->_rsm->isMixed) {
                            $element = array(0 => $element);
                        }
                        $result[] = $element;
                        $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->_resultCounter;
                        ++$this->_resultCounter;

                        if (isset($this->_hints['collection'])) {
                            $this->_hints['collection']->hydrateAdd($element);
                        }
                    }

                    // Update result pointer
                    $this->_resultPointers[$dqlAlias] = $element;

                } else {
                    // Update result pointer
                    $index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
                    $this->_resultPointers[$dqlAlias] = $result[$index];
                    /*if ($this->_rsm->isMixed) {
                        $result[] = $result[$index];
                        ++$this->_resultCounter;
                    }*/
                }
            }
        }

        // Append scalar values to mixed result sets
        if (isset($scalars)) {
            foreach ($scalars as $name => $value) {
                $result[$this->_resultCounter - 1][$name] = $value;
            }
        }
    }
}


text:
<?php

namespace Doctrine\ORM\Internal\Hydration;

class HydrationException extends \Doctrine\ORM\ORMException
{
    public static function nonUniqueResult()
    {
        return new self("The result returned by the query was not unique.");
    }

    public static function parentObjectOfRelationNotFound($alias, $parentAlias)
    {
        return new self("The parent object of entity result with alias '$alias' was not found."
                . " The parent alias is '$parentAlias'.");
    }
}

repo_name: michael-romer/zf-boilerplate

repo_language: PHP

mime_type: text/html


text:
<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.doctrine-project.org>.
 */

namespace Doctrine\ORM\Internal\Hydration;

use PDO, Doctrine\DBAL\Connection, Doctrine\ORM\Mapping\ClassMetadata;

/**
 * The ArrayHydrator produces a nested array "graph" that is often (not always)
 * interchangeable with the corresponding object graph for read-only access.
 *
 * @author Roman Borschel <roman@code-factory.org>
 * @since 1.0
 */
class ArrayHydrator extends AbstractHydrator
{
    private $_ce = array();
    private $_rootAliases = array();
    private $_isSimpleQuery = false;
    private $_identifierMap = array();
    private $_resultPointers = array();
    private $_idTemplate = array();
    private $_resultCounter = 0;

    /** @override */
    protected function _prepare()
    {
        $this->_isSimpleQuery = count($this->_rsm->aliasMap) <= 1;
        $this->_identifierMap = array();
        $this->_resultPointers = array();
        $this->_idTemplate = array();
        $this->_resultCounter = 0;
        foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
            $this->_identifierMap[$dqlAlias] = array();
            $this->_resultPointers[$dqlAlias] = array();
            $this->_idTemplate[$dqlAlias] = '';
        }
    }

    /** @override */
    protected function _hydrateAll()
    {
        $result = array();
        $cache = array();
        while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
            $this->_hydrateRow($data, $cache, $result);
        }

        return $result;
    }

    /** @override */
    protected function _hydrateRow(array $data, array &$cache, array &$result)
    {
        // 1) Initialize
        $id = $this->_idTemplate; // initialize the id-memory
        $nonemptyComponents = array();
        $rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);

        // Extract scalar values. They're appended at the end.
        if (isset($rowData['scalars'])) {
            $scalars = $rowData['scalars'];
            unset($rowData['scalars']);
            if (empty($rowData)) {
                ++$this->_resultCounter;
            }
        }

        // 2) Now hydrate the data found in the current row.
        foreach ($rowData as $dqlAlias => $data) {
            $index = false;

            if (isset($this->_rsm->parentAliasMap[$dqlAlias])) {
                // It's a joined result

                $parent = $this->_rsm->parentAliasMap[$dqlAlias];
                $path = $parent . '.' . $dqlAlias;

                // Get a reference to the right element in the result tree.
                // This element will get the associated element attached.
                if ($this->_rsm->isMixed && isset($this->_rootAliases[$parent])) {
                	$first = reset($this->_resultPointers);
                    // TODO: Exception if $key === null ?
                    $baseElement =& $this->_resultPointers[$parent][key($first)];
                } else if (isset($this->_resultPointers[$parent])) {
                    $baseElement =& $this->_resultPointers[$parent];
                } else {
                    unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
                    continue;
                }

                $relationAlias = $this->_rsm->relationMap[$dqlAlias];
                $relation = $this->_getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias];

                // Check the type of the relation (many or single-valued)
                if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
                    $oneToOne = false;
                    if (isset($nonemptyComponents[$dqlAlias])) {
                        if ( ! isset($baseElement[$relationAlias])) {
                            $baseElement[$relationAlias] = array();
                        }

                        $indexExists = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]);
                        $index = $indexExists ? $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false;
                        $indexIsValid = $index !== false ? isset($baseElement[$relationAlias][$index]) : false;

                        if ( ! $indexExists || ! $indexIsValid) {
                            $element = $data;
                            if (isset($this->_rsm->indexByMap[$dqlAlias])) {
                                $field = $this->_rsm->indexByMap[$dqlAlias];
                                $baseElement[$relationAlias][$element[$field]] = $element;
                            } else {
                                $baseElement[$relationAlias][] = $element;
                            }
                            end($baseElement[$relationAlias]);
                            $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] =
                                    key($baseElement[$relationAlias]);
                        }
                    } else if ( ! isset($baseElement[$relationAlias])) {
                        $baseElement[$relationAlias] = array();
                    }
                } else {
                    $oneToOne = true;
                    if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($baseElement[$relationAlias])) {
                        $baseElement[$relationAlias] = null;
                    } else if ( ! isset($baseElement[$relationAlias])) {
                        $baseElement[$relationAlias] = $data;
                    }
                }

                $coll =& $baseElement[$relationAlias];

                if ($coll !== null) {
                    $this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne);
                }

            } else {
                // It's a root result element

                $this->_rootAliases[$dqlAlias] = true; // Mark as root

                // Check for an existing element
                if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
                    $element = $rowData[$dqlAlias];
                    if (isset($this->_rsm->indexByMap[$dqlAlias])) {
                        $field = $this->_rsm->indexByMap[$dqlAlias];
                        if ($this->_rsm->isMixed) {
                            $result[] = array($element[$field] => $element);
                            ++$this->_resultCounter;
                        } else {
                            $result[$element[$field]] = $element;
                        }
                    } else {
                        if ($this->_rsm->isMixed) {
                            $result[] = array($element);
                            ++$this->_resultCounter;
                        } else {
                            $result[] = $element;
                        }
                    }
                    end($result);
                    $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = key($result);
                } else {
                    $index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
                    /*if ($this->_rsm->isMixed) {
                        $result[] =& $result[$index];
                        ++$this->_resultCounter;
                    }*/
                }
                $this->updateResultPointer($result, $index, $dqlAlias, false);
            }
        }

        // Append scalar values to mixed result sets
        if (isset($scalars)) {
            foreach ($scalars as $name => $value) {
                $result[$this->_resultCounter - 1][$name] = $value;
            }
        }
    }

    /**
     * Updates the result pointer for an Entity. The result pointers point to the
     * last seen instance of each Entity type. This is used for graph construction.
     *
     * @param array $coll  The element.
     * @param boolean|integer $index  Index of the element in the collection.
     * @param string $dqlAlias
     * @param boolean $oneToOne  Whether it is a single-valued association or not.
     */
    private function updateResultPointer(array &$coll, $index, $dqlAlias, $oneToOne)
    {
        if ($coll === null) {
            unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
            return;
        }
        if ($index !== false) {
            $this->_resultPointers[$dqlAlias] =& $coll[$index];
            return;
        } else {
            if ($coll) {
                if ($oneToOne) {
                    $this->_resultPointers[$dqlAlias] =& $coll;
                } else {
                    end($coll);
                    $this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
                }
            }
        }
    }

    private function _getClassMetadata($className)
    {
        if ( ! isset($this->_ce[$className])) {
            $this->_ce[$className] = $this->_em->getClassMetadata($className);
        }
        return $this->_ce[$className];
    }
}


text:
<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.doctrine-project.org>.
 */

namespace Doctrine\ORM\Internal\Hydration;

/**
 * Represents a result structure that can be iterated over, hydrating row-by-row
 * during the iteration. An IterableResult is obtained by AbstractHydrator#iterate().
 *
 * @author robo
 * @since 2.0
 */
class IterableResult implements \Iterator
{
    /**
     * @var Doctrine\ORM\Internal\Hydration\AbstractHydrator
     */
    private $_hydrator;

    /**
     * @var boolean
     */
    private $_rewinded = false;

    /**
     * @var integer
     */
    private $_key = -1;

    /**
     * @var object
     */
    private $_current = null;

    /**
     * @param Doctrine\ORM\Internal\Hydration\AbstractHydrator $hydrator
     */
    public function __construct($hydrator)
    {
        $this->_hydrator = $hydrator;
    }

    public function rewind()
    {
        if ($this->_rewinded == true) {
            throw new HydrationException("Can only iterate a Result once.");
        } else {
            $this->_current = $this->next();
            $this->_rewinded = true;
        }
    }

    /**
     * Gets the next set of results.
     *
     * @return array
     */
    public function next()
    {
        $this->_current = $this->_hydrator->hydrateRow();
        $this->_key++;
        return $this->_current;
    }

    /**
     * @return mixed
     */
    public function current()
    {
        return $this->_current;
    }

    /**
     * @return int
     */
    public function key()
    {
        return $this->_key;
    }

    /**
     * @return bool
     */
    public function valid()
    {
        return ($this->_current!=false);
    }
}


text:
<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.doctrine-project.org>.
 */

namespace Doctrine\ORM\Internal\Hydration;

use PDO,
    Doctrine\DBAL\Connection,
    Doctrine\DBAL\Types\Type,
    Doctrine\ORM\EntityManager;

/**
 * Base class for all hydrators. A hydrator is a class that provides some form
 * of transformation of an SQL result set into another structure.
 *
 * @since       2.0
 * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
 * @author      Roman Borschel <roman@code-factory.org>
 */
abstract class AbstractHydrator
{
    /** @var ResultSetMapping The ResultSetMapping. */
    protected $_rsm;

    /** @var EntityManager The EntityManager instance. */
    protected $_em;

    /** @var AbstractPlatform The dbms Platform instance */
    protected $_platform;

    /** @var UnitOfWork The UnitOfWork of the associated EntityManager. */
    protected $_uow;

    /** @var array The cache used during row-by-row hydration. */
    protected $_cache = array();

    /** @var Statement The statement that provides the data to hydrate. */
    protected $_stmt;

    /** @var array The query hints. */
    protected $_hints;

    /**
     * Initializes a new instance of a class derived from <tt>AbstractHydrator</tt>.
     *
     * @param Doctrine\ORM\EntityManager $em The EntityManager to use.
     */
    public function __construct(EntityManager $em)
    {
        $this->_em = $em;
        $this->_platform = $em->getConnection()->getDatabasePlatform();
        $this->_uow = $em->getUnitOfWork();
    }

    /**
     * Initiates a row-by-row hydration.
     *
     * @param object $stmt
     * @param object $resultSetMapping
     * @return IterableResult
     */
    public function iterate($stmt, $resultSetMapping, array $hints = array())
    {
        $this->_stmt = $stmt;
        $this->_rsm = $resultSetMapping;
        $this->_hints = $hints;
        $this->_prepare();
        return new IterableResult($this);
    }

    /**
     * Hydrates all rows returned by the passed statement instance at once.
     *
     * @param object $stmt
     * @param object $resultSetMapping
     * @return mixed
     */
    public function hydrateAll($stmt, $resultSetMapping, array $hints = array())
    {
        $this->_stmt = $stmt;
        $this->_rsm = $resultSetMapping;
        $this->_hints = $hints;
        $this->_prepare();
        $result = $this->_hydrateAll();
        $this->_cleanup();
        return $result;
    }

    /**
     * Hydrates a single row returned by the current statement instance during
     * row-by-row hydration with {@link iterate()}.
     *
     * @return mixed
     */
    public function hydrateRow()
    {
        $row = $this->_stmt->fetch(PDO::FETCH_ASSOC);
        if ( ! $row) {
            $this->_cleanup();
            return false;
        }
        $result = array();
        $this->_hydrateRow($row, $this->_cache, $result);
        return $result;
    }

    /**
     * Excutes one-time preparation tasks, once each time hydration is started
     * through {@link hydrateAll} or {@link iterate()}.
     */
    protected function _prepare()
    {}

    /**
     * Excutes one-time cleanup tasks at the end of a hydration that was initiated
     * through {@link hydrateAll} or {@link iterate()}.
     */
    protected function _cleanup()
    {
        $this->_rsm = null;
        $this->_stmt->closeCursor();
        $this->_stmt = null;
    }

    /**
     * Hydrates a single row from the current statement instance.
     *
     * Template method.
     *
     * @param array $data The row data.
     * @param array $cache The cache to use.
     * @param mixed $result The result to fill.
     */
    protected function _hydrateRow(array $data, array &$cache, array &$result)
    {
        throw new HydrationException("_hydrateRow() not implemented by this hydrator.");
    }

    /**
     * Hydrates all rows from the current statement instance at once.
     */
    abstract protected function _hydrateAll();

    /**
     * Processes a row of the result set.
     * Used for identity-based hydration (HYDRATE_OBJECT and HYDRATE_ARRAY).
     * Puts the elements of a result row into a new array, grouped by the class
     * they belong to. The column names in the result set are mapped to their
     * field names during this procedure as well as any necessary conversions on
     * the values applied.
     *
     * @return array  An array with all the fields (name => value) of the data row,
     *                grouped by their component alias.
     */
    protected function _gatherRowData(array $data, array &$cache, array &$id, array &$nonemptyComponents)
    {
        $rowData = array();

        foreach ($data as $key => $value) {
            // Parse each column name only once. Cache the results.
            if ( ! isset($cache[$key])) {
                if (isset($this->_rsm->scalarMappings[$key])) {
                    $cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
                    $cache[$key]['isScalar'] = true;
                } else if (isset($this->_rsm->fieldMappings[$key])) {
                    $fieldName = $this->_rsm->fieldMappings[$key];
                    $classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]);
                    $cache[$key]['fieldName'] = $fieldName;
                    $cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
                    $cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName);
                    $cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
                } else if (!isset($this->_rsm->metaMappings[$key])) {
                    // this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
                    // maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
                    continue;
                } else {
                    // Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
                    $fieldName = $this->_rsm->metaMappings[$key];
                    $cache[$key]['isMetaColumn'] = true;
                    $cache[$key]['fieldName'] = $fieldName;
                    $cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
                    $classMetadata = $this->_em->getClassMetadata($this->_rsm->aliasMap[$cache[$key]['dqlAlias']]);
                    $cache[$key]['isIdentifier'] = isset($this->_rsm->isIdentifierColumn[$cache[$key]['dqlAlias']][$key]);
                }
            }

            if (isset($cache[$key]['isScalar'])) {
                $rowData['scalars'][$cache[$key]['fieldName']] = $value;
                continue;
            }

            $dqlAlias = $cache[$key]['dqlAlias'];

            if ($cache[$key]['isIdentifier']) {
                $id[$dqlAlias] .= '|' . $value;
            }

            if (isset($cache[$key]['isMetaColumn'])) {
                if (!isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) || $value !== null) {
                    $rowData[$dqlAlias][$cache[$key]['fieldName']] = $value;
                }
                continue;
            }

            // in an inheritance hierachy the same field could be defined several times.
            // We overwrite this value so long we dont have a non-null value, that value we keep.
            // Per definition it cannot be that a field is defined several times and has several values.
            if (isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) && $value === null) {
                continue;
            }

            $rowData[$dqlAlias][$cache[$key]['fieldName']] = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);

            if ( ! isset($nonemptyComponents[$dqlAlias]) && $value !== null) {
                $nonemptyComponents[$dqlAlias] = true;
            }
        }

        return $rowData;
    }

    /**
     * Processes a row of the result set.
     * Used for HYDRATE_SCALAR. This is a variant of _gatherRowData() that
     * simply converts column names to field names and properly converts the
     * values according to their types. The resulting row has the same number
     * of elements as before.
     *
     * @param array $data
     * @param array $cache
     * @return array The processed row.
     */
    protected function _gatherScalarRowData(&$data, &$cache)
    {
        $rowData = array();

        foreach ($data as $key => $value) {
            // Parse each column name only once. Cache the results.
            if ( ! isset($cache[$key])) {
                if (isset($this->_rsm->scalarMappings[$key])) {
                    $cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
                    $cache[$key]['isScalar'] = true;
                } else if (isset($this->_rsm->fieldMappings[$key])) {
                    $fieldName = $this->_rsm->fieldMappings[$key];
                    $classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]);
                    $cache[$key]['fieldName'] = $fieldName;
                    $cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
                    $cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
                } else if (!isset($this->_rsm->metaMappings[$key])) {
                    // this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
                    // maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
                    continue;
                } else {
                    // Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
                    $cache[$key]['isMetaColumn'] = true;
                    $cache[$key]['fieldName'] = $this->_rsm->metaMappings[$key];
                    $cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
                }
            }

            $fieldName = $cache[$key]['fieldName'];

            if (isset($cache[$key]['isScalar'])) {
                $rowData[$fieldName] = $value;
            } else if (isset($cache[$key]['isMetaColumn'])) {
                $rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $value;
            } else {
                $rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $cache[$key]['type']
                        ->convertToPHPValue($value, $this->_platform);
            }
        }

        return $rowData;
    }

    protected function registerManaged($class, $entity, $data)
    {
        if ($class->isIdentifierComposite) {
            $id = array();
            foreach ($class->identifier as $fieldName) {
                if (isset($class->associationMappings[$fieldName])) {
                    $id[$fieldName] = $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']];
                } else {
                    $id[$fieldName] = $data[$fieldName];
                }
            }
        } else {
            if (isset($class->associationMappings[$class->identifier[0]])) {
                $id = array($class->identifier[0] => $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']]);
            } else {
                $id = array($class->identifier[0] => $data[$class->identifier[0]]);
            }
        }
        $this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
    }
}


text:
<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.doctrine-project.org>.
 */

namespace Doctrine\ORM\Internal\Hydration;

use \PDO;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Query;

class SimpleObjectHydrator extends AbstractHydrator
{
    /**
     * @var ClassMetadata
     */
    private $class;

    private $declaringClasses = array();

    protected function _hydrateAll()
    {
        $result = array();
        $cache = array();

        while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
            $this->_hydrateRow($row, $cache, $result);
        }

        $this->_em->getUnitOfWork()->triggerEagerLoads();

        return $result;
    }

    protected function _prepare()
    {
        if (count($this->_rsm->aliasMap) == 1) {
            $this->class = $this->_em->getClassMetadata(reset($this->_rsm->aliasMap));
            if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
                foreach ($this->_rsm->declaringClasses AS $column => $class) {
                    $this->declaringClasses[$column] = $this->_em->getClassMetadata($class);
                }
            }
        } else {
            throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping not containing exactly one object result.");
        }
        if ($this->_rsm->scalarMappings) {
            throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains scalar mappings.");
        }
    }

    protected function _hydrateRow(array $sqlResult, array &$cache, array &$result)
    {
        $data = array();
        if ($this->class->inheritanceType == ClassMetadata::INHERITANCE_TYPE_NONE) {
            foreach ($sqlResult as $column => $value) {

                if (!isset($cache[$column])) {
                    if (isset($this->_rsm->fieldMappings[$column])) {
                        $cache[$column]['name'] = $this->_rsm->fieldMappings[$column];
                        $cache[$column]['field'] = true;
                    } else {
                        $cache[$column]['name'] = $this->_rsm->metaMappings[$column];
                    }
                }

                if (isset($cache[$column]['field'])) {
                    $value = Type::getType($this->class->fieldMappings[$cache[$column]['name']]['type'])
                                    ->convertToPHPValue($value, $this->_platform);
                }
                $data[$cache[$column]['name']] = $value;
            }
            $entityName = $this->class->name;
        } else {
            $discrColumnName = $this->_platform->getSQLResultCasing($this->class->discriminatorColumn['name']);
            $entityName = $this->class->discriminatorMap[$sqlResult[$discrColumnName]];
            unset($sqlResult[$discrColumnName]);
            foreach ($sqlResult as $column => $value) {
                if (!isset($cache[$column])) {
                    if (isset($this->_rsm->fieldMappings[$column])) {
                        $field = $this->_rsm->fieldMappings[$column];
                        $class = $this->declaringClasses[$column];
                        if ($class->name == $entityName || is_subclass_of($entityName, $class->name)) {
                            $cache[$column]['name'] = $field;
                            $cache[$column]['class'] = $class;
                        }
                    } else if (isset($this->_rsm->relationMap[$column])) {
                        if ($this->_rsm->relationMap[$column] == $entityName || is_subclass_of($entityName, $this->_rsm->relationMap[$column])) {
                            $cache[$column]['name'] = $field;
                        }
                    } else {
                        $cache[$column]['name'] = $this->_rsm->metaMappings[$column];
                    }
                }

                if (isset($cache[$column]['class'])) {
                    $value = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type'])
                                    ->convertToPHPValue($value, $this->_platform);
                }

                // the second and part is to prevent overwrites in case of multiple
                // inheritance classes using the same property name (See AbstractHydrator)
                if (isset($cache[$column]) && (!isset($data[$cache[$column]['name']]) || $value !== null)) {
                    $data[$cache[$column]['name']] = $value;
                }
            }
        }

        if (isset($this->_hints[Query::HINT_REFRESH_ENTITY])) {
            $this->registerManaged($this->class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
        }

        $result[] = $this->_em->getUnitOfWork()->createEntity($entityName, $data, $this->_hints);
    }
}


text:
<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.doctrine-project.org>.
 */

namespace Doctrine\ORM\Internal\Hydration;

use Doctrine\DBAL\Connection;

/**
 * Hydrator that produces flat, rectangular results of scalar data.
 * The created result is almost the same as a regular SQL result set, except
 * that column names are mapped to field names and data type conversions take place.
 *
 * @author Roman Borschel <roman@code-factory.org>
 * @since 2.0
 */
class ScalarHydrator extends AbstractHydrator
{
    /** @override */
    protected function _hydrateAll()
    {
        $result = array();
        $cache = array();
        while ($data = $this->_stmt->fetch(\PDO::FETCH_ASSOC)) {
            $result[] = $this->_gatherScalarRowData($data, $cache);
        }
        return $result;
    }

    /** @override */
    protected function _hydrateRow(array $data, array &$cache, array &$result)
    {
        $result[] = $this->_gatherScalarRowData($data, $cache);
    }
}


text:
<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.doctrine-project.org>.
 */

namespace Doctrine\ORM\Query;

/**
 * This class is used to generate DQL expressions via a set of PHP static functions
 *
 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
 * @link    www.doctrine-project.org
 * @since   2.0
 * @author  Guilherme Blanco <guilhermeblanco@hotmail.com>
 * @author  Jonathan Wage <jonwage@gmail.com>
 * @author  Roman Borschel <roman@code-factory.org>
 * @author  Benjamin Eberlei <kontakt@beberlei.de>
 * @todo Rename: ExpressionBuilder
 */
class Expr
{
    /**
     * Creates a conjunction of the given boolean expressions.
     *
     * Example:
     *
     *     [php]
     *     // (u.type = ?1) AND (u.role = ?2)
     *     $expr->andX('u.type = ?1', 'u.role = ?2'));
     *
     * @param mixed $x Optional clause. Defaults = null, but requires
     *                 at least one defined when converting to string.
     * @return Expr\Andx
     */
    public function andX($x = null)
    {
        return new Expr\Andx(func_get_args());
    }

    /**
     * Creates a disjunction of the given boolean expressions.
     *
     * Example:
     *
     *     [php]
     *     // (u.type = ?1) OR (u.role = ?2)
     *     $q->where($q->expr()->orX('u.type = ?1', 'u.role = ?2'));
     *
     * @param mixed $x Optional clause. Defaults = null, but requires
     *                 at least one defined when converting to string.
     * @return Expr\Orx
     */
    public function orX($x = null)
    {
        return new Expr\Orx(func_get_args());
    }

    /**
     * Creates an ASCending order expression.
     *
     * @param $sort
     * @return OrderBy
     */
    public function asc($expr)
    {
        return new Expr\OrderBy($expr, 'ASC');
    }

    /**
     * Creates a DESCending order expression.
     *
     * @param $sort
     * @return OrderBy
     */
    public function desc($expr)
    {
        return new Expr\OrderBy($expr, 'DESC');
    }

    /**
     * Creates an equality comparison expression with the given arguments.
     *
     * First argument is considered the left expression and the second is the right expression.
     * When converted to string, it will generated a <left expr> = <right expr>. Example:
     *
     *     [php]
     *     // u.id = ?1
     *     $expr->eq('u.id', '?1');
     *
     * @param mixed $x Left expression
     * @param mixed $y Right expression
     * @return Expr\Comparison
     */
    public function eq($x, $y)
    {
        return new Expr\Comparison($x, Expr\Comparison::EQ, $y);
    }

    /**
     * Creates an instance of Expr\Comparison, with the given arguments.
     * First argument is considered the left expression and the second is the right expression.
     * When converted to string, it will generated a <left expr> <> <right expr>. Example:
     *
     *     [php]
     *     // u.id <> ?1
     *     $q->where($q->expr()->neq('u.id', '?1'));
     *
     * @param mixed $x Left expression
     * @param mixed $y Right expression
     * @return Expr\Comparison
     */
    public function neq($x, $y)
    {
        return new Expr\Comparison($x, Expr\Comparison::NEQ, $y);
    }

    /**
     * Creates an instance of Expr\Comparison, with the given arguments.
     * First argument is considered the left expression and the second is the right expression.
     * When converted to string, it will generated a <left expr> < <right expr>. Example:
     *
     *     [php]
     *     // u.id < ?1
     *     $q->where($q->expr()->lt('u.id', '?1'));
     *
     * @param mixed $x Left expression
     * @param mixed $y Right expression
     * @return Expr\Comparison
     */
    public function lt($x, $y)
    {
        return new Expr\Comparison($x, Expr\Comparison::LT, $y);
    }

    /**
     * Creates an instance of Expr\Comparison, with the given arguments.
     * First argument is considered the left expression and the second is the right expression.
     * When converted to string, it will generated a <left expr> <= <right expr>. Example:
     *
     *     [php]
     *     // u.id <= ?1
     *     $q->where($q->expr()->lte('u.id', '?1'));
     *
     * @param mixed $x Left expression
     * @param mixed $y Right expression
     * @return Expr\Comparison
     */
    public function lte($x, $y)
    {
        return new Expr\Comparison($x, Expr\Comparison::LTE, $y);
    }

    /**
     * Creates an instance of Expr\Comparison, with the given arguments.
     * First argument is considered the left expression and the second is the right expression.
     * When converted to string, it will generated a <left expr> > <right expr>. Example:
     *
     *     [php]
     *     // u.id > ?1
     *     $q->where($q->expr()->gt('u.id', '?1'));
     *
     * @param mixed $x Left expression
     * @param mixed $y Right expression
     * @return Expr\Comparison
     */
    public function gt($x, $y)
    {
        return new Expr\Comparison($x, Expr\Comparison::GT, $y);
    }

    /**
     * Creates an instance of Expr\Comparison, with the given arguments.
     * First argument is considered the left expression and the second is the right expression.
     * When converted to string, it will generated a <left expr> >= <right expr>. Example:
     *
     *     [php]
     *     // u.id >= ?1
     *     $q->where($q->expr()->gte('u.id', '?1'));
     *
     * @param mixed $x Left expression
     * @param mixed $y Right expression
     * @return Expr\Comparison
     */
    public function gte($x, $y)
    {
        return new Expr\Comparison($x, Expr\Comparison::GTE, $y);
    }

    /**
     * Creates an instance of AVG() function, with the given argument.
     *
     * @param mixed $x Argument to be used in AVG() function.
     * @return Expr\Func
     */
    public function avg($x)
    {
        return new Expr\Func('AVG', array($x));
    }

    /**
     * Creates an instance of MAX() function, with the given argument.
     *
     * @param mixed $x Argument to be used in MAX() function.
     * @return Expr\Func
     */
    public function max($x)
    {
        return new Expr\Func('MAX', array($x));
    }

    /**
     * Creates an instance of MIN() function, with the given argument.
     *
     * @param mixed $x Argument to be used in MIN() function.
     * @return Expr\Func
     */
    public function min($x)
    {
        return new Expr\Func('MIN', array($x));
    }

    /**
     * Creates an instance of COUNT() function, with the given argument.
     *
     * @param mixed $x Argument to be used in COUNT() function.
     * @return Expr\Func
     */
    public function count($x)
    {
        return new Expr\Func('COUNT', array($x));
    }

    /**
     * Creates an instance of COUNT(DISTINCT) function, with the given argument.
     *
     * @param mixed $x Argument to be used in COUNT(DISTINCT) function.
     * @return string
     */
    public function countDistinct($x)
    {
        return 'COUNT(DISTINCT ' . implode(', ', func_get_args()) . ')';
    }

    /**
     * Creates an instance of EXISTS() function, with the given DQL Subquery.
     *
     * @param mixed $subquery DQL Subquery to be used in EXISTS() function.
     * @return Expr\Func
     */
    public function exists($subquery)
    {
        return new Expr\Func('EXISTS', array($subquery));
    }

    /**
     * Creates an instance of ALL() function, with the given DQL Subquery.
     *
     * @param mixed $subquery DQL Subquery to be used in ALL() function.
     * @return Expr\Func
     */
    public function all($subquery)
    {
        return new Expr\Func('ALL', array($subquery));
    }

    /**
     * Creates a SOME() function expression with the given DQL subquery.
     *
     * @param mixed $subquery DQL Subquery to be used in SOME() function.
     * @return Expr\Func
     */
    public function some($subquery)
    {
        return new Expr\Func('SOME', array($subquery));
    }

    /**
     * Creates an ANY() function expression with the given DQL subquery.
     *
     * @param mixed $subquery DQL Subquery to be used in ANY() function.
     * @return Expr\Func
     */
    public function any($subquery)
    {
        return new Expr\Func('ANY', array($subquery));
    }

    /**
     * Creates a negation expression of the given restriction.
     *
     * @param mixed $restriction Restriction to be used in NOT() function.
     * @return Expr\Func
     */
    public function not($restriction)
    {
        return new Expr\Func('NOT', array($restriction));
    }

    /**
     * Creates an ABS() function expression with the given argument.
     *
     * @param mixed $x Argument to be used in ABS() function.
     * @return Expr\Func
     */
    public function abs($x)
    {
        return new Expr\Func('ABS', array($x));
    }

    /**
     * Creates a product mathematical expression with the given arguments.
     *
     * First argument is considered the left expression and the second is the right expression.
     * When converted to string, it will generated a <left expr> * <right expr>. Example:
     *
     *     [php]
     *     // u.salary * u.percentAnualSalaryIncrease
     *     $q->expr()->prod('u.salary', 'u.percentAnualSalaryIncrease')
     *
     * @param mixed $x Left expression
     * @param mixed $y Right expression
     * @return Expr\Math
     */
    public function prod($x, $y)
    {
        return new Expr\Math($x, '*', $y);
    }

    /**
     * Creates a difference mathematical expression with the given arguments.
     * First argument is considered the left expression and the second is the right expression.
     * When converted to string, it will generated a <left expr> - <right expr>. Example:
     *
     *     [php]
     *     // u.monthlySubscriptionCount - 1
     *     $q->expr()->diff('u.monthlySubscriptionCount', '1')
     *
     * @param mixed $x Left expression
     * @param mixed $y Right expression
     * @return Expr\Math
     */
    public function diff($x, $y)
    {
        return new Expr\Math($x, '-', $y);
    }

    /**
     * Creates a sum mathematical expression with the given arguments.
     * First argument is considered the left expression and the second is the right expression.
     * When converted to string, it will generated a <left expr> + <right expr>. Example:
     *
     *     [php]
     *     // u.numChildren + 1
     *     $q->expr()->diff('u.numChildren', '1')
     *
     * @param mixed $x Left expression
     * @param mixed $y Right expression
     * @return Expr\Math
     */
    public function sum($x, $y)
    {
        return new Expr\Math($x, '+', $y);
    }

    /**
     * Creates a quotient mathematical expression with the given arguments.
     * First argument is considered the left expression and the second is the right expression.
     * When converted to string, it will generated a <left expr> / <right expr>. Example:
     *
     *     [php]
     *     // u.total / u.period
     *     $expr->quot('u.total', 'u.period')
     *
     * @param mixed $x Left expression
     * @param mixed $y Right expression
     * @return Expr\Math
     */
    public function quot($x, $y)
    {
        return new Expr\Math($x, '/', $y);
    }

    /**
     * Creates a SQRT() function expression with the given argument.
     *
     * @param mixed $x Argument to be used in SQRT() function.
     * @return Expr\Func
     */
    public function sqrt($x)
    {
        return new Expr\Func('SQRT', array($x));
    }

    /**
     * Creates an IN() expression with the given arguments.
     *
     * @param string $x Field in string format to be restricted by IN() function
     * @param mixed $y Argument to be used in IN() function.
     * @return Expr\Func
     */
    public function in($x, $y)
    {
        if (is_array($y)) {
            foreach ($y as &$literal) {
                if ( ! ($literal instanceof Expr\Literal)) {
                    $literal = $this->_quoteLiteral($literal);
                }
            }
        }
        return new Expr\Func($x . ' IN', (array) $y);
    }

    /**
     * Creates a NOT IN() expression with the given arguments.
     *
     * @param string $x Field in string format to be restricted by NOT IN() function
     * @param mixed $y Argument to be used in NOT IN() function.
     * @return Expr\Func
     */
    public function notIn($x, $y)
    {
        if (is_array($y)) {
            foreach ($y as &$literal) {
                if ( ! ($literal instanceof Expr\Literal)) {
                    $literal = $this->_quoteLiteral($literal);
                }
            }
        }
        return new Expr\Func($x . ' NOT IN', (array) $y);
    }

    /**
     * Creates an IS NULL expression with the given arguments.
     *
     * @param string $x Field in string format to be restricted by IS NULL
     * @return string
     */
    public function isNull($x)
    {
        return $x . ' IS NULL';
    }

    /**
     * Creates an IS NOT NULL expression with the given arguments.
     *
     * @param string $x Field in string format to be restricted by IS NOT NULL
     * @return string
     */
    public function isNotNull($x)
    {
        return $x . ' IS NOT NULL';
    }

    /**
     * Creates a LIKE() comparison expression with the given arguments.
     *
     * @param string $x Field in string format to be inspected by LIKE() comparison.
     * @param mixed $y Argument to be used in LIKE() comparison.
     * @return Expr\Comparison
     */
    public function like($x, $y)
    {
        return new Expr\Comparison($x, 'LIKE', $y);
    }

    /**
     * Creates a CONCAT() function expression with the given arguments.
     *
     * @param mixed $x First argument to be used in CONCAT() function.
     * @param mixed $x Second argument to be used in CONCAT() function.
     * @return Expr\Func
     */
    public function concat($x, $y)
    {
        return new Expr\Func('CONCAT', array($x, $y));
    }

    /**
     * Creates a SUBSTRING() function expression with the given arguments.
     *
     * @param mixed $x Argument to be used as string to be cropped by SUBSTRING() function.
     * @param integer $from Initial offset to start cropping string. May accept negative values.
     * @param integer $len Length of crop. May accept negative values.
     * @return Expr\Func
     */
    public function substring($x, $from, $len = null)
    {
        $args = array($x, $from);
        if (null !== $len) {
            $args[] = $len;
        }
        return new Expr\Func('SUBSTRING', $args);
    }

    /**
     * Creates a LOWER() function expression with the given argument.
     *
     * @param mixed $x Argument to be used in LOWER() function.
     * @return Expr\Func A LOWER function expression.
     */
    public function lower($x)
    {
        return new Expr\Func('LOWER', array($x));
    }

    /**
     * Creates an UPPER() function expression with the given argument.
     *
     * @param mixed $x Argument to be used in UPPER() function.
     * @return Expr\Func An UPPER function expression.
     */
    public function upper($x)
    {
        return new Expr\Func('UPPER', array($x));
    }

    /**
     * Creates a LENGTH() function expression with the given argument.
     *
     * @param mixed $x Argument to be used as argument of LENGTH() function.
     * @return Expr\Func A LENGTH function expression.
     */
    public function length($x)
    {
        return new Expr\Func('LENGTH', array($x));
    }

    /**
     * Creates a literal expression of the given argument.
     *
     * @param mixed $literal Argument to be converted to literal.
     * @return Expr\Literal
     */
    public function literal($literal)
    {
        return new Expr\Literal($this->_quoteLiteral($literal));
    }

    /**
     * Quotes a literal value, if necessary, according to the DQL syntax.
     *
     * @param mixed $literal The literal value.
     * @return string
     */
    private function _quoteLiteral($literal)
    {
        if (is_numeric($literal) && !is_string($literal)) {
            return (string) $literal;
        } else {
            return "'" . str_replace("'", "''", $literal) . "'";
        }
    }

    /**
     * Creates an instance of BETWEEN() function, with the given argument.
     *
     * @param mixed $val Valued to be inspected by range values.
     * @param integer $x Starting range value to be used in BETWEEN() function.
     * @param integer $y End point value to be used in BETWEEN() function.
     * @return Expr\Func A BETWEEN expression.
     */
    public function between($val, $x, $y)
    {
        return $val . ' BETWEEN ' . $x . ' AND ' . $y;
    }

    /**
     * Creates an instance of TRIM() function, with the given argument.
     *
     * @param mixed $x Argument to be used as argument of TRIM() function.
     * @return Expr\Func a TRIM expression.
     */
    public function trim($x)
    {
        return new Expr\Func('TRIM', $x);
    }
}


text:
<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.doctrine-project.org>.
 */

namespace Doctrine\ORM\Query;

/**
 * A ResultSetMapping describes how a result set of an SQL query maps to a Doctrine result.
 *
 * IMPORTANT NOTE:
 * The properties of this class are only public for fast internal READ access and to (drastically)
 * reduce the size of serialized instances for more effective caching due to better (un-)serialization
 * performance.
 *
 * <b>Users should use the public methods.</b>
 *
 * @author Roman Borschel <roman@code-factory.org>
 * @since 2.0
 * @todo Think about whether the number of lookup maps can be reduced.
 */
class ResultSetMapping
{
    /**
     * Whether the result is mixed (contains scalar values together with field values).
     *
     * @ignore
     * @var boolean
     */
    public $isMixed = false;
    /**
     * Maps alias names to class names.
     *
     * @ignore
     * @var array
     */
    public $aliasMap = array();
    /**
     * Maps alias names to related association field names.
     *
     * @ignore
     * @var array
     */
    public $relationMap = array();
    /**
     * Maps alias names to parent alias names.
     *
     * @ignore
     * @var array
     */
    public $parentAliasMap = array();
    /**
     * Maps column names in the result set to field names for each class.
     *
     * @ignore
     * @var array
     */
    public $fieldMappings = array();
    /**
     * Maps column names in the result set to the alias/field name to use in the mapped result.
     *
     * @ignore
     * @var array
     */
    public $scalarMappings = array();
    /**
     * Maps column names of meta columns (foreign keys, discriminator columns, ...) to field names.
     *
     * @ignore
     * @var array
     */
    public $metaMappings = array();
    /**
     * Maps column names in the result set to the alias they belong to.
     *
     * @ignore
     * @var array
     */
    public $columnOwnerMap = array();
    /**
     * List of columns in the result set that are used as discriminator columns.
     *
     * @ignore
     * @var array
     */
    public $discriminatorColumns = array();
    /**
     * Maps alias names to field names that should be used for indexing.
     *
     * @ignore
     * @var array
     */
    public $indexByMap = array();
    /**
     * Map from column names to class names that declare the field the column is mapped to.
     *
     * @ignore
     * @var array
     */
    public $declaringClasses = array();

    /**
     * This is necessary to hydrate derivate foreign keys correctly.
     *
     * @var array
     */
    public $isIdentifierColumn = array();

    /**
     * Adds an entity result to this ResultSetMapping.
     *
     * @param string $class The class name of the entity.
     * @param string $alias The alias for the class. The alias must be unique among all entity
     *                      results or joined entity results within this ResultSetMapping.
     * @todo Rename: addRootEntity
     */
    public function addEntityResult($class, $alias)
    {
        $this->aliasMap[$alias] = $class;
    }

    /**
     * Sets a discriminator column for an entity result or joined entity result.
     * The discriminator column will be used to determine the concrete class name to
     * instantiate.
     *
     * @param string $alias The alias of the entity result or joined entity result the discriminator
     *                      column should be used for.
     * @param string $discrColumn The name of the discriminator column in the SQL result set.
     * @todo Rename: addDiscriminatorColumn
     */
    public function setDiscriminatorColumn($alias, $discrColumn)
    {
        $this->discriminatorColumns[$alias] = $discrColumn;
        $this->columnOwnerMap[$discrColumn] = $alias;
    }

    /**
     * Sets a field to use for indexing an entity result or joined entity result.
     *
     * @param string $alias The alias of an entity result or joined entity result.
     * @param string $fieldName The name of the field to use for indexing.
     */
    public function addIndexBy($alias, $fieldName)
    {
        $this->indexByMap[$alias] = $fieldName;
    }

    /**
     * Checks whether an entity result or joined entity result with a given alias has
     * a field set for indexing.
     *
     * @param string $alias
     * @return boolean
     * @todo Rename: isIndexed($alias)
     */
    public function hasIndexBy($alias)
    {
        return isset($this->indexByMap[$alias]);
    }

    /**
     * Checks whether the column with the given name is mapped as a field result
     * as part of an entity result or joined entity result.
     *
     * @param string $columnName The name of the column in the SQL result set.
     * @return boolean
     * @todo Rename: isField
     */
    public function isFieldResult($columnName)
    {
        return isset($this->fieldMappings[$columnName]);
    }

    /**
     * Adds a field to the result that belongs to an entity or joined entity.
     *
     * @param string $alias The alias of the root entity or joined entity to which the field belongs.
     * @param string $columnName The name of the column in the SQL result set.
     * @param string $fieldName The name of the field on the declaring class.
     * @param string $declaringClass The name of the class that declares/owns the specified field.
     *                               When $alias refers to a superclass in a mapped hierarchy but
     *                               the field $fieldName is defined on a subclass, specify that here.
     *                               If not specified, the field is assumed to belong to the class
     *                               designated by $alias.
     * @todo Rename: addField
     */
    public function addFieldResult($alias, $columnName, $fieldName, $declaringClass = null)
    {
        // column name (in result set) => field name
        $this->fieldMappings[$columnName] = $fieldName;
        // column name => alias of owner
        $this->columnOwnerMap[$columnName] = $alias;
        // field name => class name of declaring class
        $this->declaringClasses[$columnName] = $declaringClass ?: $this->aliasMap[$alias];
        if ( ! $this->isMixed && $this->scalarMappings) {
            $this->isMixed = true;
        }
    }

    /**
     * Adds a joined entity result.
     *
     * @param string $class The class name of the joined entity.
     * @param string $alias The unique alias to use for the joined entity.
     * @param string $parentAlias The alias of the entity result that is the parent of this joined result.
     * @param object $relation The association field that connects the parent entity result with the joined entity result.
     * @todo Rename: addJoinedEntity
     */
    public function addJoinedEntityResult($class, $alias, $parentAlias, $relation)
    {
        $this->aliasMap[$alias] = $class;
        $this->parentAliasMap[$alias] = $parentAlias;
        $this->relationMap[$alias] = $relation;
    }

    /**
     * Adds a scalar result mapping.
     *
     * @param string $columnName The name of the column in the SQL result set.
     * @param string $alias The result alias with which the scalar result should be placed in the result structure.
     * @todo Rename: addScalar
     */
    public function addScalarResult($columnName, $alias)
    {
        $this->scalarMappings[$columnName] = $alias;
        if ( ! $this->isMixed && $this->fieldMappings) {
            $this->isMixed = true;
        }
    }

    /**
     * Checks whether a column with a given name is mapped as a scalar result.
     *
     * @param string $columName The name of the column in the SQL result set.
     * @return boolean
     * @todo Rename: isScalar
     */
    public function isScalarResult($columnName)
    {
        return isset($this->scalarMappings[$columnName]);
    }

    /**
     * Gets the name of the class of an entity result or joined entity result,
     * identified by the given unique alias.
     *
     * @param string $alias
     * @return string
     */
    public function getClassName($alias)
    {
        return $this->aliasMap[$alias];
    }

    /**
     * Gets the field alias for a column that is mapped as a scalar value.
     *
     * @param string $columnName The name of the column in the SQL result set.
     * @return string
     */
    public function getScalarAlias($columnName)
    {
        return $this->scalarMappings[$columnName];
    }

    /**
     * Gets the name of the class that owns a field mapping for the specified column.
     *
     * @param string $columnName
     * @return string
     */
    public function getDeclaringClass($columnName)
    {
        return $this->declaringClasses[$columnName];
    }

    /**
     *
     * @param string $alias
     * @return AssociationMapping
     */
    public function getRelation($alias)
    {
        return $this->relationMap[$alias];
    }

    /**
     *
     * @param string $alias
     * @return boolean
     */
    public function isRelation($alias)
    {
        return isset($this->relationMap[$alias]);
    }

    /**
     * Gets the alias of the class that owns a field mapping for the specified column.
     *
     * @param string $columnName
     * @return string
     */
    public function getEntityAlias($columnName)
    {
        return $this->columnOwnerMap[$columnName];
    }

    /**
     * Gets the parent alias of the given alias.
     *
     * @param string $alias
     * @return string
     */
    public function getParentAlias($alias)
    {
        return $this->parentAliasMap[$alias];
    }

    /**
     * Checks whether the given alias has a parent alias.
     *
     * @param string $alias
     * @return boolean
     */
    public function hasParentAlias($alias)
    {
        return isset($this->parentAliasMap[$alias]);
    }

    /**
     * Gets the field name for a column name.
     *
     * @param string $columnName
     * @return string
     */
    public function getFieldName($columnName)
    {
        return $this->fieldMappings[$columnName];
    }

    /**
     *
     * @return array
     */
    public function getAliasMap()
    {
        return $this->aliasMap;
    }

    /**
     * Gets the number of different entities that appear in the mapped result.
     *
     * @return integer
     */
    public function getEntityResultCount()
    {
        return count($this->aliasMap);
    }

    /**
     * Checks whether this ResultSetMapping defines a mixed result.
     * Mixed results can only occur in object and array (graph) hydration. In such a
     * case a mixed result means that scalar values are mixed with objects/array in
     * the result.
     *
     * @return boolean
     */
    public function isMixedResult()
    {
        return $this->isMixed;
    }

    /**
     * Adds a meta column (foreign key or discriminator column) to the result set.
     *
     * @param string $alias
     * @param string $columnName
     * @param string $fieldName
     * @param bool
     */
    public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false)
    {
        $this->metaMappings[$columnName] = $fieldName;
        $this->columnOwnerMap[$columnName] = $alias;
        if ($isIdentifierColumn) {
            $this->isIdentifierColumn[$alias][$columnName] = true;
        }
    }
}


text:
<?php
/*
 *  $Id$
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.phpdoctrine.org>.
 */

namespace Doctrine\ORM\Query;

/**
 * A parse tree printer for Doctrine Query Language parser.
 *
 * @author      Janne Vanhala <jpvanhal@cc.hut.fi>
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
 * @link        http://www.phpdoctrine.org
 * @since       2.0
 * @version     $Revision$
 */
class Printer
{
    /**
     * Current indentation level
     *
     * @var int
     */
    protected $_indent = 0;

    /**
     * Defines whether parse tree is printed (default, false) or not (true).
     *
     * @var bool
     */
    protected $_silent;

    /**
     * Constructs a new parse tree printer.
     *
     * @param bool $silent Parse tree will not be printed if true.
     */
    public function __construct($silent = false)
    {
        $this->_silent = $silent;
    }

    /**
     * Prints an opening parenthesis followed by production name and increases
     * indentation level by one.
     *
     * This method is called before executing a production.
     *
     * @param string $name production name
     */
    public function startProduction($name)
    {
        $this->println('(' . $name);
        $this->_indent++;
    }

    /**
     * Decreases indentation level by one and prints a closing parenthesis.
     *
     * This method is called after executing a production.
     */
    public function endProduction()
    {
        $this->_indent--;
        $this->println(')');
    }

    /**
     * Prints text indented with spaces depending on current indentation level.
     *
     * @param string $str text
     */
    public function println($str)
    {
        if ( ! $this->_silent) {
            echo str_repeat('    ', $this->_indent), $str, "\n";
        }
    }
}