"Fossies" - the Fresh Open Source Software Archive

Member "icinga-web-1.14.1/lib/doctrine/lib/Doctrine/Query/Abstract.php" (19 Dec 2017, 66437 Bytes) of package /linux/misc/old/icinga-web-1.14.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "Abstract.php" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.13.1_vs_1.14.0.

    1 <?php
    2 /*
    3  *  $Id: Query.php 1393 2007-05-19 17:49:16Z zYne $
    4  *
    5  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    6  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    7  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    8  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    9  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   10  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   11  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   12  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   13  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   14  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   15  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   16  *
   17  * This software consists of voluntary contributions made by many individuals
   18  * and is licensed under the LGPL. For more information, see
   19  * <http://www.doctrine-project.org>.
   20  */
   21 
   22 /**
   23  * Doctrine_Query_Abstract
   24  *
   25  * @package     Doctrine
   26  * @subpackage  Query
   27  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
   28  * @link        www.doctrine-project.org
   29  * @since       1.0
   30  * @version     $Revision: 1393 $
   31  * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
   32  * @todo        See {@link Doctrine_Query}
   33  */
   34 abstract class Doctrine_Query_Abstract
   35 {
   36     /**
   37      * QUERY TYPE CONSTANTS
   38      */
   39 
   40     /**
   41      * constant for SELECT queries
   42      */
   43     const SELECT = 0;
   44 
   45     /**
   46      * constant for DELETE queries
   47      */
   48     const DELETE = 1;
   49 
   50     /**
   51      * constant for UPDATE queries
   52      */
   53     const UPDATE = 2;
   54 
   55     /**
   56      * constant for INSERT queries
   57      */
   58     const INSERT = 3;
   59 
   60     /**
   61      * constant for CREATE queries
   62      */
   63     const CREATE = 4;
   64 
   65     /** @todo document the query states (and the transitions between them). */
   66     /**
   67      * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.
   68      */
   69     const STATE_CLEAN  = 1;
   70 
   71     /**
   72      * A query object is in state DIRTY when it has DQL parts that have not yet been
   73      * parsed/processed.
   74      */
   75     const STATE_DIRTY  = 2;
   76 
   77     /**
   78      * A query is in DIRECT state when ... ?
   79      */
   80     const STATE_DIRECT = 3;
   81 
   82     /**
   83      * A query object is on LOCKED state when ... ?
   84      */
   85     const STATE_LOCKED = 4;
   86 
   87     /**
   88      * @var array  Table alias map. Keys are SQL aliases and values DQL aliases.
   89      */
   90     protected $_tableAliasMap = array();
   91 
   92     /**
   93      * @var Doctrine_View  The view object used by this query, if any.
   94      */
   95     protected $_view;
   96 
   97     /**
   98      * @var integer $_state   The current state of this query.
   99      */
  100     protected $_state = Doctrine_Query::STATE_CLEAN;
  101 
  102     /**
  103      * @var array $_params  The parameters of this query.
  104      */
  105     protected $_params = array('exec' => array(),
  106                                'join' => array(),
  107                                'where' => array(),
  108                                'set' => array(),
  109                                'having' => array());
  110 
  111     /**
  112      * @var array $_execParams The parameters passed to connection statement
  113      */
  114     protected $_execParams = array();
  115 
  116     /* Caching properties */
  117     /**
  118      * @var Doctrine_Cache_Interface  The cache driver used for caching result sets.
  119      */
  120     protected $_resultCache;
  121 
  122     /**
  123      * @var string  Key to use for result cache entry in the cache driver
  124      */
  125     protected $_resultCacheHash;
  126 
  127     /**
  128      * @var boolean $_expireResultCache  A boolean value that indicates whether or not
  129      *                                   expire the result cache.
  130      */
  131     protected $_expireResultCache = false;
  132     protected $_resultCacheTTL;
  133 
  134     /**
  135      * @var Doctrine_Cache_Interface  The cache driver used for caching queries.
  136      */
  137     protected $_queryCache;
  138     protected $_expireQueryCache = false;
  139     protected $_queryCacheTTL;
  140 
  141 
  142     /**
  143      * @var Doctrine_Connection  The connection used by this query object.
  144      */
  145     protected $_conn;
  146 
  147     /**
  148      * @var bool Whether or not a connection was passed to this query object to use
  149      */
  150     protected $_passedConn = false;
  151 
  152     /**
  153      * @var array $_sqlParts  The SQL query string parts. Filled during the DQL parsing process.
  154      */
  155     protected $_sqlParts = array(
  156             'select'    => array(),
  157             'distinct'  => false,
  158             'forUpdate' => false,
  159             'from'      => array(),
  160             'set'       => array(),
  161             'join'      => array(),
  162             'where'     => array(),
  163             'groupby'   => array(),
  164             'having'    => array(),
  165             'orderby'   => array(),
  166             'limit'     => false,
  167             'offset'    => false,
  168             );
  169 
  170     /**
  171      * @var array $_dqlParts    an array containing all DQL query parts; @see Doctrine_Query::getDqlPart()
  172      */
  173     protected $_dqlParts = array(
  174                             'from'      => array(),
  175                             'select'    => array(),
  176                             'forUpdate' => false,
  177                             'set'       => array(),
  178                             'join'      => array(),
  179                             'where'     => array(),
  180                             'groupby'   => array(),
  181                             'having'    => array(),
  182                             'orderby'   => array(),
  183                             'limit'     => array(),
  184                             'offset'    => array(),
  185                             );
  186 
  187 
  188     /**
  189      * @var array $_queryComponents   Two dimensional array containing the components of this query,
  190      *                                informations about their relations and other related information.
  191      *                                The components are constructed during query parsing.
  192      *
  193      *      Keys are component aliases and values the following:
  194      *
  195      *          table               table object associated with given alias
  196      *
  197      *          relation            the relation object owned by the parent
  198      *
  199      *          parent              the alias of the parent
  200      *
  201      *          agg                 the aggregates of this component
  202      *
  203      *          map                 the name of the column / aggregate value this
  204      *                              component is mapped to a collection
  205      */
  206     protected $_queryComponents = array();
  207 
  208     /**
  209      * Stores the root DQL alias
  210      *
  211      * @var string
  212      */
  213     protected $_rootAlias = '';
  214     
  215     /**
  216      * @var integer $type                   the query type
  217      *
  218      * @see Doctrine_Query::* constants
  219      */
  220     protected $_type = self::SELECT;
  221 
  222     /**
  223      * @var Doctrine_Hydrator   The hydrator object used to hydrate query results.
  224      */
  225     protected $_hydrator;
  226 
  227     /**
  228      * @var Doctrine_Query_Tokenizer  The tokenizer that is used during the query parsing process.
  229      */
  230     protected $_tokenizer;
  231 
  232     /**
  233      * @var Doctrine_Query_Parser  The parser that is used for query parsing.
  234      */
  235     protected $_parser;
  236 
  237     /**
  238      * @var array $_tableAliasSeeds         A simple array keys representing table aliases and values
  239      *                                      table alias seeds. The seeds are used for generating short table
  240      *                                      aliases.
  241      */
  242     protected $_tableAliasSeeds = array();
  243 
  244     /**
  245      * @var array $_options                 an array of options
  246      */
  247     protected $_options    = array(
  248         'hydrationMode'      => Doctrine_Core::HYDRATE_RECORD
  249     );
  250 
  251     /**
  252      * @var boolean
  253      */
  254     protected $_isLimitSubqueryUsed = false;
  255 
  256     /**
  257      * @var array components used in the DQL statement
  258      */
  259     protected $_components;
  260 
  261     /**
  262      * @var bool Boolean variable for whether or not the preQuery process has been executed
  263      */
  264     protected $_preQueried = false;
  265 
  266     /**
  267      * Constructor.
  268      *
  269      * @param Doctrine_Connection  The connection object the query will use.
  270      * @param Doctrine_Hydrator_Abstract  The hydrator that will be used for generating result sets.
  271      */
  272     public function __construct(Doctrine_Connection $connection = null,
  273             Doctrine_Hydrator_Abstract $hydrator = null)
  274     {
  275         if ($connection === null) {
  276             $connection = Doctrine_Manager::getInstance()->getCurrentConnection();
  277         } else {
  278             $this->_passedConn = true;
  279         }
  280         if ($hydrator === null) {
  281             $hydrator = new Doctrine_Hydrator();
  282         }
  283         $this->_conn = $connection;
  284         $this->_hydrator = $hydrator;
  285         $this->_tokenizer = new Doctrine_Query_Tokenizer();
  286         $this->_resultCacheTTL = $this->_conn->getAttribute(Doctrine_Core::ATTR_RESULT_CACHE_LIFESPAN);
  287         $this->_queryCacheTTL = $this->_conn->getAttribute(Doctrine_Core::ATTR_QUERY_CACHE_LIFESPAN);
  288     }
  289 
  290     /**
  291      * Set the connection this query object should use
  292      *
  293      * @param Doctrine_Connection $connection
  294      * @return void
  295      */
  296     public function setConnection(Doctrine_Connection $connection)
  297     {
  298         $this->_passedConn = true;
  299         $this->_conn = $connection;
  300     }
  301 
  302     /**
  303      * setOption
  304      *
  305      * @param string $name      option name
  306      * @param string $value     option value
  307      * @return Doctrine_Query   this object
  308      */
  309     public function setOption($name, $value)
  310     {
  311         if ( ! isset($this->_options[$name])) {
  312             throw new Doctrine_Query_Exception('Unknown option ' . $name);
  313         }
  314         $this->_options[$name] = $value;
  315     }
  316 
  317     /**
  318      * hasSqlTableAlias
  319      * whether or not this object has given tableAlias
  320      *
  321      * @param string $tableAlias    the table alias to be checked
  322      * @return boolean              true if this object has given alias, otherwise false
  323      */
  324     public function hasSqlTableAlias($sqlTableAlias)
  325     {
  326         return (isset($this->_tableAliasMap[$sqlTableAlias]));
  327     }
  328 
  329     /**
  330      * getTableAliasMap
  331      * returns all table aliases
  332      *
  333      * @return array        table aliases as an array
  334      */
  335     public function getTableAliasMap()
  336     {
  337         return $this->_tableAliasMap;
  338     }
  339 
  340     /**
  341      * getDql
  342      * returns the DQL query that is represented by this query object.
  343      *
  344      * the query is built from $_dqlParts
  345      *
  346      * @return string   the DQL query
  347      */
  348     public function getDql()
  349     {
  350         $q = '';
  351         if ($this->_type == self::SELECT) {
  352             $q .= ( ! empty($this->_dqlParts['select'])) ? 'SELECT ' . implode(', ', $this->_dqlParts['select']) : '';
  353             $q .= ( ! empty($this->_dqlParts['from'])) ? ' FROM ' . implode(' ', $this->_dqlParts['from']) : '';
  354         } else if ($this->_type == self::DELETE) {
  355             $q .= 'DELETE';
  356             $q .= ( ! empty($this->_dqlParts['from'])) ? ' FROM ' . implode(' ', $this->_dqlParts['from']) : '';
  357         } else if ($this->_type == self::UPDATE) {
  358             $q .= 'UPDATE ';
  359             $q .= ( ! empty($this->_dqlParts['from'])) ? implode(' ', $this->_dqlParts['from']) : '';
  360             $q .= ( ! empty($this->_dqlParts['set'])) ? ' SET ' . implode(' ', $this->_dqlParts['set']) : '';
  361         }
  362         $q .= ( ! empty($this->_dqlParts['where'])) ? ' WHERE ' . implode(' ', $this->_dqlParts['where']) : '';
  363         $q .= ( ! empty($this->_dqlParts['groupby'])) ? ' GROUP BY ' . implode(', ', $this->_dqlParts['groupby']) : '';
  364         $q .= ( ! empty($this->_dqlParts['having'])) ? ' HAVING ' . implode(' AND ', $this->_dqlParts['having']) : '';
  365         $q .= ( ! empty($this->_dqlParts['orderby'])) ? ' ORDER BY ' . implode(', ', $this->_dqlParts['orderby']) : '';
  366         $q .= ( ! empty($this->_dqlParts['limit'])) ? ' LIMIT ' . implode(' ', $this->_dqlParts['limit']) : '';
  367         $q .= ( ! empty($this->_dqlParts['offset'])) ? ' OFFSET ' . implode(' ', $this->_dqlParts['offset']) : '';
  368 
  369         return $q;
  370     }
  371 
  372     /**
  373      * getSqlQueryPart
  374      * gets an SQL query part from the SQL query part array
  375      *
  376      * @param string $name          the name of the query part to be set
  377      * @param string $part          query part string
  378      * @throws Doctrine_Query_Exception   if trying to set unknown query part
  379      * @return mixed     this object
  380      */
  381     public function getSqlQueryPart($part)
  382     {
  383         if ( ! isset($this->_sqlParts[$part])) {
  384             throw new Doctrine_Query_Exception('Unknown SQL query part ' . $part);
  385         }
  386         return $this->_sqlParts[$part];
  387     }
  388 
  389     /**
  390      * setSqlQueryPart
  391      * sets an SQL query part in the SQL query part array
  392      *
  393      * @param string $name          the name of the query part to be set
  394      * @param string $part          query part string
  395      * @throws Doctrine_Query_Exception   if trying to set unknown query part
  396      * @return Doctrine_Query     this object
  397      */
  398     public function setSqlQueryPart($name, $part)
  399     {
  400         if ( ! isset($this->_sqlParts[$name])) {
  401             throw new Doctrine_Query_Exception('Unknown query part ' . $name);
  402         }
  403 
  404         if ($name !== 'limit' && $name !== 'offset') {
  405             if (is_array($part)) {
  406                 $this->_sqlParts[$name] = $part;
  407             } else {
  408                 $this->_sqlParts[$name] = array($part);
  409             }
  410         } else {
  411             $this->_sqlParts[$name] = $part;
  412         }
  413 
  414         return $this;
  415     }
  416 
  417     /**
  418      * addSqlQueryPart
  419      * adds an SQL query part to the SQL query part array
  420      *
  421      * @param string $name          the name of the query part to be added
  422      * @param string $part          query part string
  423      * @throws Doctrine_Query_Exception   if trying to add unknown query part
  424      * @return Doctrine_Query     this object
  425      */
  426     public function addSqlQueryPart($name, $part)
  427     {
  428         if ( ! isset($this->_sqlParts[$name])) {
  429             throw new Doctrine_Query_Exception('Unknown query part ' . $name);
  430         }
  431         if (is_array($part)) {
  432             $this->_sqlParts[$name] = array_merge($this->_sqlParts[$name], $part);
  433         } else {
  434             $this->_sqlParts[$name][] = $part;
  435         }
  436         return $this;
  437     }
  438 
  439     /**
  440      * removeSqlQueryPart
  441      * removes a query part from the query part array
  442      *
  443      * @param string $name          the name of the query part to be removed
  444      * @throws Doctrine_Query_Exception   if trying to remove unknown query part
  445      * @return Doctrine_Query     this object
  446      */
  447     public function removeSqlQueryPart($name)
  448     {
  449         if ( ! isset($this->_sqlParts[$name])) {
  450             throw new Doctrine_Query_Exception('Unknown query part ' . $name);
  451         }
  452 
  453         if ($name == 'limit' || $name == 'offset' || $name == 'forUpdate') {
  454             $this->_sqlParts[$name] = false;
  455         } else {
  456             $this->_sqlParts[$name] = array();
  457         }
  458 
  459         return $this;
  460     }
  461 
  462     /**
  463      * removeDqlQueryPart
  464      * removes a dql query part from the dql query part array
  465      *
  466      * @param string $name          the name of the query part to be removed
  467      * @throws Doctrine_Query_Exception   if trying to remove unknown query part
  468      * @return Doctrine_Query     this object
  469      */
  470     public function removeDqlQueryPart($name)
  471     {
  472         if ( ! isset($this->_dqlParts[$name])) {
  473             throw new Doctrine_Query_Exception('Unknown query part ' . $name);
  474         }
  475 
  476         if ($name == 'limit' || $name == 'offset') {
  477             $this->_dqlParts[$name] = false;
  478         } else {
  479             $this->_dqlParts[$name] = array();
  480         }
  481 
  482         return $this;
  483     }
  484 
  485     /**
  486      * Get raw array of parameters for query and all parts.
  487      *
  488      * @return array $params
  489      */
  490     public function getParams()
  491     {
  492         return $this->_params;
  493     }
  494 
  495     /**
  496      * Get flattened array of parameters for query.
  497      * Used internally and used to pass flat array of params to the database.
  498      *
  499      * @param array $params
  500      * @return void
  501      */
  502     public function getFlattenedParams($params = array())
  503     {
  504         return array_merge(
  505             (array) $params, (array) $this->_params['exec'], 
  506             $this->_params['join'], $this->_params['set'],
  507             $this->_params['where'], $this->_params['having']
  508         );
  509     }
  510 
  511     /**
  512      * getInternalParams
  513      *
  514      * @return array
  515      */
  516     public function getInternalParams($params = array())
  517     {
  518         return array_merge($params, $this->_execParams);
  519     }
  520 
  521     /**
  522      * setParams
  523      *
  524      * @param array $params
  525      */
  526     public function setParams(array $params = array())
  527     {
  528         $this->_params = $params;
  529     }
  530     
  531     /**
  532      * getCountQueryParams
  533      * Retrieves the parameters for count query
  534      *
  535      * @return array Parameters array
  536      */
  537     public function getCountQueryParams($params = array())
  538     {
  539         if ( ! is_array($params)) {
  540             $params = array($params);
  541         }
  542 
  543         $this->_params['exec'] = $params;
  544 
  545         $params = array_merge($this->_params['join'], $this->_params['where'], $this->_params['having'], $this->_params['exec']);
  546 
  547         $this->fixArrayParameterValues($params);
  548 
  549         return $this->_execParams;
  550     }
  551 
  552     /**
  553      * @nodoc
  554      */
  555     public function fixArrayParameterValues($params = array())
  556     {
  557         $i = 0;
  558     
  559         foreach ($params as $param) {
  560             if (is_array($param)) {
  561                 $c = count($param);
  562 
  563                 array_splice($params, $i, 1, $param);
  564                 
  565                 $i += $c;
  566             } else {
  567                 $i++;
  568             }
  569         }
  570         
  571         $this->_execParams = $params;
  572     }
  573 
  574     /**
  575      * setView
  576      * sets a database view this query object uses
  577      * this method should only be called internally by doctrine
  578      *
  579      * @param Doctrine_View $view       database view
  580      * @return void
  581      */
  582     public function setView(Doctrine_View $view)
  583     {
  584         $this->_view = $view;
  585     }
  586 
  587     /**
  588      * getView
  589      * returns the view associated with this query object (if any)
  590      *
  591      * @return Doctrine_View        the view associated with this query object
  592      */
  593     public function getView()
  594     {
  595         return $this->_view;
  596     }
  597 
  598     /**
  599      * limitSubqueryUsed
  600      *
  601      * @return boolean
  602      */
  603     public function isLimitSubqueryUsed()
  604     {
  605         return $this->_isLimitSubqueryUsed;
  606     }
  607 
  608     /**
  609      * Returns the inheritance condition for the passed componentAlias
  610      * If no component alias is specified it defaults to the root component
  611      *
  612      * This function is used to append a SQL condition to models which have inheritance mapping
  613      * The condition is applied to the FROM component in the WHERE, but the condition is applied to
  614      * JOINS in the ON condition and not the WHERE
  615      *
  616      * @return string $str  SQL condition string
  617      */
  618     public function getInheritanceCondition($componentAlias)
  619     {
  620         $map = $this->_queryComponents[$componentAlias]['table']->inheritanceMap;
  621 
  622         // No inheritance map so lets just return
  623         if (empty($map)) {
  624           return;
  625         }
  626 
  627         $tableAlias = $this->getSqlTableAlias($componentAlias);
  628 
  629         if ($this->_type !== Doctrine_Query::SELECT) {
  630             $tableAlias = '';
  631         } else {
  632             $tableAlias .= '.';
  633         }
  634 
  635         // Fix for 2015: loop through whole inheritanceMap to add all   
  636         // keyFields for inheritance (and not only the first) 
  637         $retVal = ""; 
  638         $count = 0; 
  639          
  640         foreach ($map as $field => $value) { 
  641             if ($count++ > 0) {
  642                 $retVal .= ' AND ';
  643             }
  644 
  645             $identifier = $this->_conn->quoteIdentifier($tableAlias . $field); 
  646             $retVal .= $identifier . ' = ' . $this->_conn->quote($value);
  647         }
  648 
  649         return $retVal;
  650     }
  651 
  652     /**
  653      * getSqlTableAlias
  654      * some database such as Oracle need the identifier lengths to be < ~30 chars
  655      * hence Doctrine creates as short identifier aliases as possible
  656      *
  657      * this method is used for the creation of short table aliases, its also
  658      * smart enough to check if an alias already exists for given component (componentAlias)
  659      *
  660      * @param string $componentAlias    the alias for the query component to search table alias for
  661      * @param string $tableName         the table name from which the table alias is being created
  662      * @return string                   the generated / fetched short alias
  663      */
  664     public function getSqlTableAlias($componentAlias, $tableName = null)
  665     {
  666         $alias = array_search($componentAlias, $this->_tableAliasMap);
  667         
  668         if ($alias !== false) {
  669             return $alias;
  670         }
  671 
  672         if ($tableName === null) { 
  673             throw new Doctrine_Query_Exception("Couldn't get short alias for " . $componentAlias);
  674         }
  675 
  676         return $this->generateSqlTableAlias($componentAlias, $tableName);
  677     }
  678 
  679     /**
  680      * generateNewSqlTableAlias
  681      * generates a new alias from given table alias
  682      *
  683      * @param string $tableAlias    table alias from which to generate the new alias from
  684      * @return string               the created table alias
  685      */
  686     public function generateNewSqlTableAlias($oldAlias)
  687     {
  688         if (isset($this->_tableAliasMap[$oldAlias])) {
  689             // generate a new alias
  690             $name = substr($oldAlias, 0, 1);
  691             $i    = ((int) substr($oldAlias, 1));
  692 
  693             // Fix #1530: It was reaching unexistent seeds index
  694             if ( ! isset($this->_tableAliasSeeds[$name])) {
  695                 $this->_tableAliasSeeds[$name] = 1;
  696             }
  697 
  698             $newIndex  = ($this->_tableAliasSeeds[$name] + (($i == 0) ? 1 : $i));
  699 
  700             return $name . $newIndex;
  701         }
  702 
  703         return $oldAlias;
  704     }
  705 
  706     /**
  707      * getSqlTableAliasSeed
  708      * returns the alias seed for given table alias
  709      *
  710      * @param string $tableAlias    table alias that identifies the alias seed
  711      * @return integer              table alias seed
  712      */
  713     public function getSqlTableAliasSeed($sqlTableAlias)
  714     {
  715         if ( ! isset($this->_tableAliasSeeds[$sqlTableAlias])) {
  716             return 0;
  717         }
  718         return $this->_tableAliasSeeds[$sqlTableAlias];
  719     }
  720 
  721     /**
  722      * hasAliasDeclaration
  723      * whether or not this object has a declaration for given component alias
  724      *
  725      * @param string $componentAlias    the component alias the retrieve the declaration from
  726      * @return boolean
  727      */
  728     public function hasAliasDeclaration($componentAlias)
  729     {
  730         return isset($this->_queryComponents[$componentAlias]);
  731     }
  732 
  733     /**
  734      * getQueryComponent
  735      * get the declaration for given component alias
  736      *
  737      * @param string $componentAlias    the component alias the retrieve the declaration from
  738      * @return array                    the alias declaration
  739      */
  740     public function getQueryComponent($componentAlias)
  741     {
  742         if ( ! isset($this->_queryComponents[$componentAlias])) {
  743             throw new Doctrine_Query_Exception('Unknown component alias ' . $componentAlias);
  744         }
  745 
  746         return $this->_queryComponents[$componentAlias];
  747     }
  748 
  749     /**
  750      * copySubqueryInfo
  751      * copy aliases from another Hydrate object
  752      *
  753      * this method is needed by DQL subqueries which need the aliases
  754      * of the parent query
  755      *
  756      * @param Doctrine_Hydrate $query   the query object from which the
  757      *                                  aliases are copied from
  758      * @return Doctrine_Query         this object
  759      */
  760     public function copySubqueryInfo(Doctrine_Query_Abstract $query)
  761     {
  762         $this->_params =& $query->_params;
  763         $this->_tableAliasMap =& $query->_tableAliasMap;
  764         $this->_queryComponents =& $query->_queryComponents;
  765         $this->_tableAliasSeeds = $query->_tableAliasSeeds;
  766         return $this;
  767     }
  768 
  769     /**
  770      * getRootAlias
  771      * returns the alias of the root component
  772      *
  773      * @return array
  774      */
  775     public function getRootAlias()
  776     {
  777         if ( ! $this->_queryComponents) {
  778             $this->getSqlQuery(array(), false);
  779         }
  780         
  781         return $this->_rootAlias;
  782     }
  783 
  784     /**
  785      * getRootDeclaration
  786      * returns the root declaration
  787      *
  788      * @return array
  789      */
  790     public function getRootDeclaration()
  791     {
  792         $map = $this->_queryComponents[$this->_rootAlias];
  793         return $map;
  794     }
  795 
  796     /**
  797      * getRoot
  798      * returns the root component for this object
  799      *
  800      * @return Doctrine_Table       root components table
  801      */
  802     public function getRoot()
  803     {
  804         $map = $this->_queryComponents[$this->_rootAlias];
  805 
  806         if ( ! isset($map['table'])) {
  807             throw new Doctrine_Query_Exception('Root component not initialized.');
  808         }
  809 
  810         return $map['table'];
  811     }
  812 
  813     /**
  814      * generateSqlTableAlias
  815      * generates a table alias from given table name and associates
  816      * it with given component alias
  817      *
  818      * @param string $componentAlias    the component alias to be associated with generated table alias
  819      * @param string $tableName         the table name from which to generate the table alias
  820      * @return string                   the generated table alias
  821      */
  822     public function generateSqlTableAlias($componentAlias, $tableName)
  823     {
  824         preg_match('/([^_|\d])/', $tableName, $matches);
  825         $char = strtolower($matches[0]);
  826 
  827         $alias = $char;
  828 
  829         if ( ! isset($this->_tableAliasSeeds[$alias])) {
  830             $this->_tableAliasSeeds[$alias] = 1;
  831         }
  832 
  833         while (isset($this->_tableAliasMap[$alias])) {
  834             if ( ! isset($this->_tableAliasSeeds[$alias])) {
  835                 $this->_tableAliasSeeds[$alias] = 1;
  836             }
  837             $alias = $char . ++$this->_tableAliasSeeds[$alias];
  838         }
  839 
  840         $this->_tableAliasMap[$alias] = $componentAlias;
  841 
  842         return $alias;
  843     }
  844 
  845     /**
  846      * getComponentAlias
  847      * get component alias associated with given table alias
  848      *
  849      * @param string $sqlTableAlias    the SQL table alias that identifies the component alias
  850      * @return string               component alias
  851      */
  852     public function getComponentAlias($sqlTableAlias)
  853     {
  854         $sqlTableAlias = trim($sqlTableAlias, '[]`"');
  855         if ( ! isset($this->_tableAliasMap[$sqlTableAlias])) {
  856             throw new Doctrine_Query_Exception('Unknown table alias ' . $sqlTableAlias);
  857         }
  858         return $this->_tableAliasMap[$sqlTableAlias];
  859     }
  860 
  861     /**
  862      * calculateQueryCacheHash
  863      * calculate hash key for query cache
  864      *
  865      * @return string    the hash
  866      */
  867     public function calculateQueryCacheHash()
  868     {
  869         $dql = $this->getDql();
  870         $hash = md5($dql . var_export($this->_pendingJoinConditions, true) . 'DOCTRINE_QUERY_CACHE_SALT');
  871         return $hash;
  872     }
  873 
  874     /**
  875      * calculateResultCacheHash
  876      * calculate hash key for result cache
  877      *
  878      * @param array $params
  879      * @return string    the hash
  880      */
  881     public function calculateResultCacheHash($params = array())
  882     {
  883         $dql = $this->getDql();
  884         $conn = $this->getConnection();
  885         $params = $this->getFlattenedParams($params);
  886         $hash = md5($this->_hydrator->getHydrationMode() . $conn->getName() . $conn->getOption('dsn') . $dql . var_export($this->_pendingJoinConditions, true) . var_export($params, true));
  887         return $hash;
  888     }
  889 
  890     /**
  891      * Get the result cache hash/key. Returns key set with useResultCache()
  892      * or generates a unique key from the query automatically.
  893      *
  894      * @param array $params
  895      * @return string $hash
  896      */
  897     public function getResultCacheHash($params = array())
  898     {
  899       if ($this->_resultCacheHash) {
  900           return $this->_resultCacheHash;
  901       } else {
  902           return $this->calculateResultCacheHash($params);
  903       }
  904     }
  905 
  906     /**
  907      * _execute
  908      *
  909      * @param array $params
  910      * @return PDOStatement  The executed PDOStatement.
  911      */
  912     protected function _execute($params)
  913     {
  914         // Apply boolean conversion in DQL params
  915         $params = $this->_conn->convertBooleans($params);
  916 
  917         foreach ($this->_params as $k => $v) {
  918             $this->_params[$k] = $this->_conn->convertBooleans($v);
  919         }
  920 
  921         $dqlParams = $this->getFlattenedParams($params);
  922 
  923         // Check if we're not using a Doctrine_View
  924         if ( ! $this->_view) {
  925             if ($this->_queryCache !== false && ($this->_queryCache || $this->_conn->getAttribute(Doctrine_Core::ATTR_QUERY_CACHE))) {
  926                 $queryCacheDriver = $this->getQueryCacheDriver();
  927                 $hash = $this->calculateQueryCacheHash();
  928                 $cached = $queryCacheDriver->fetch($hash);
  929 
  930                 // If we have a cached query...
  931                 if ($cached) {
  932                     // Rebuild query from cache
  933                     $query = $this->_constructQueryFromCache($cached);
  934                     
  935                     // Assign building/execution specific params
  936                     $this->_params['exec'] = $params;
  937             
  938                     // Initialize prepared parameters array
  939                     $this->_execParams = $this->getFlattenedParams();
  940                     
  941                     // Fix possible array parameter values in SQL params
  942                     $this->fixArrayParameterValues($this->getInternalParams());
  943                 } else {
  944                     // Generate SQL or pick already processed one
  945                     $query = $this->getSqlQuery($params);
  946 
  947                     // Check again because getSqlQuery() above could have flipped the _queryCache flag
  948                     // if this query contains the limit sub query algorithm we don't need to cache it
  949                     if ($this->_queryCache !== false && ($this->_queryCache || $this->_conn->getAttribute(Doctrine_Core::ATTR_QUERY_CACHE))) {
  950                         // Convert query into a serialized form
  951                         $serializedQuery = $this->getCachedForm($query);
  952 
  953                         // Save cached query
  954                         $queryCacheDriver->save($hash, $serializedQuery, $this->getQueryCacheLifeSpan());
  955                     }
  956                 }
  957             } else {
  958                 $query = $this->getSqlQuery($params);
  959             }
  960         } else {
  961             $query = $this->_view->getSelectSql();
  962         }
  963         
  964         // Get prepared SQL params for execution
  965         $params = $this->getInternalParams();
  966 
  967         if ($this->isLimitSubqueryUsed() &&
  968                 $this->_conn->getAttribute(Doctrine_Core::ATTR_DRIVER_NAME) !== 'mysql') {
  969             $params = array_merge((array) $params, (array) $params);
  970         }
  971 
  972         if ($this->_type !== self::SELECT) {
  973             return $this->_conn->exec($query, $params);
  974         }
  975 
  976         $stmt = $this->_conn->execute($query, $params);
  977 
  978         $this->_params['exec'] = array();
  979 
  980         return $stmt;
  981     }
  982 
  983     /**
  984      * execute
  985      * executes the query and populates the data set
  986      *
  987      * @param array $params
  988      * @return Doctrine_Collection            the root collection
  989      */
  990     public function execute($params = array(), $hydrationMode = null)
  991     {
  992         // Clean any possible processed params
  993         $this->_execParams = array();
  994 
  995         if (empty($this->_dqlParts['from']) && empty($this->_sqlParts['from'])) {
  996             throw new Doctrine_Query_Exception('You must have at least one component specified in your from.');
  997         }
  998 
  999         $dqlParams = $this->getFlattenedParams($params);
 1000 
 1001         $this->_preQuery($dqlParams);
 1002 
 1003         if ($hydrationMode !== null) {
 1004             $this->_hydrator->setHydrationMode($hydrationMode);
 1005         }
 1006 
 1007         $hydrationMode = $this->_hydrator->getHydrationMode();
 1008 
 1009         if ($this->_resultCache && $this->_type == self::SELECT) {
 1010             $cacheDriver = $this->getResultCacheDriver();
 1011             $hash = $this->getResultCacheHash($params);
 1012             $cached = ($this->_expireResultCache) ? false : $cacheDriver->fetch($hash);
 1013 
 1014             if ($cached === false) {
 1015                 // cache miss
 1016                 $stmt = $this->_execute($params);
 1017                 $this->_hydrator->setQueryComponents($this->_queryComponents);
 1018                 $result = $this->_hydrator->hydrateResultSet($stmt, $this->_tableAliasMap);
 1019 
 1020                 $cached = $this->getCachedForm($result);
 1021                 $cacheDriver->save($hash, $cached, $this->getResultCacheLifeSpan());
 1022             } else {
 1023                 $result = $this->_constructQueryFromCache($cached);
 1024             }
 1025         } else {
 1026             $stmt = $this->_execute($params);
 1027 
 1028             if (is_integer($stmt)) {
 1029                 $result = $stmt;
 1030             } else {
 1031                 $this->_hydrator->setQueryComponents($this->_queryComponents);
 1032                 if ($this->_type == self::SELECT && $hydrationMode == Doctrine_Core::HYDRATE_ON_DEMAND) {
 1033                     $hydrationDriver = $this->_hydrator->getHydratorDriver($hydrationMode, $this->_tableAliasMap);
 1034                     $result = new Doctrine_Collection_OnDemand($stmt, $hydrationDriver, $this->_tableAliasMap); 
 1035                 } else {
 1036                     $result = $this->_hydrator->hydrateResultSet($stmt, $this->_tableAliasMap);
 1037                 }
 1038             }
 1039         }
 1040         if ($this->getConnection()->getAttribute(Doctrine_Core::ATTR_AUTO_FREE_QUERY_OBJECTS)) {
 1041             $this->free();
 1042         }
 1043 
 1044         return $result;
 1045     }
 1046 
 1047     /**
 1048      * Blank template method free(). Override to be used to free query object memory
 1049      */
 1050     public function free()
 1051     { 
 1052     }
 1053 
 1054     /**
 1055      * Get the dql call back for this query
 1056      *
 1057      * @return array $callback
 1058      */
 1059     protected function _getDqlCallback()
 1060     {
 1061         $callback = false;
 1062         if ( ! empty($this->_dqlParts['from'])) {
 1063             switch ($this->_type) {
 1064                 case self::DELETE:
 1065                     $callback = array(
 1066                         'callback' => 'preDqlDelete',
 1067                         'const' => Doctrine_Event::RECORD_DQL_DELETE
 1068                     );
 1069                 break;
 1070                 case self::UPDATE:
 1071                     $callback = array(
 1072                         'callback' => 'preDqlUpdate',
 1073                         'const' => Doctrine_Event::RECORD_DQL_UPDATE
 1074                     );
 1075                 break;
 1076                 case self::SELECT:
 1077                     $callback = array(
 1078                         'callback' => 'preDqlSelect',
 1079                         'const' => Doctrine_Event::RECORD_DQL_SELECT
 1080                     );
 1081                 break;
 1082             }
 1083         }
 1084 
 1085         return $callback;
 1086     }
 1087 
 1088     /**
 1089      * Pre query method which invokes the pre*Query() methods on the model instance or any attached
 1090      * record listeners
 1091      *
 1092      * @return void
 1093      */
 1094     protected function _preQuery($params = array())
 1095     {
 1096         if ( ! $this->_preQueried && $this->getConnection()->getAttribute(Doctrine_Core::ATTR_USE_DQL_CALLBACKS)) {
 1097             $this->_preQueried = true;
 1098 
 1099             $callback = $this->_getDqlCallback();
 1100 
 1101             // if there is no callback for the query type, then we can return early
 1102             if ( ! $callback) {
 1103                 return;
 1104             }
 1105 
 1106             foreach ($this->_getDqlCallbackComponents($params) as $alias => $component) {
 1107                 $table = $component['table'];
 1108                 $record = $table->getRecordInstance();
 1109 
 1110                 // Trigger preDql*() callback event
 1111                 $params = array('component' => $component, 'alias' => $alias);
 1112                 $event = new Doctrine_Event($record, $callback['const'], $this, $params);
 1113 
 1114                 $record->{$callback['callback']}($event);
 1115                 $table->getRecordListener()->{$callback['callback']}($event);
 1116             }
 1117         }
 1118 
 1119         // Invoke preQuery() hook on Doctrine_Query for child classes which implement this hook
 1120         $this->preQuery();
 1121     }
 1122 
 1123     /**
 1124      * Returns an array of components to execute the query callbacks for
 1125      *
 1126      * @param  array $params
 1127      * @return array $components
 1128      */
 1129     protected function _getDqlCallbackComponents($params = array())
 1130     {
 1131         $componentsBefore = array();
 1132         if ($this->isSubquery()) {
 1133             $componentsBefore = $this->getQueryComponents();
 1134         }
 1135 
 1136         $copy = $this->copy();
 1137         $copy->getSqlQuery($params, false);
 1138         $componentsAfter = $copy->getQueryComponents();
 1139 
 1140         $this->_rootAlias = $copy->getRootAlias();
 1141 
 1142         $copy->free();
 1143 
 1144         if ($componentsBefore !== $componentsAfter) {
 1145             return array_diff($componentsAfter, $componentsBefore);
 1146         } else {
 1147             return $componentsAfter;
 1148         }
 1149     }
 1150 
 1151     /**
 1152      * Blank hook methods which can be implemented in Doctrine_Query child classes
 1153      *
 1154      * @return void
 1155      */
 1156     public function preQuery()
 1157     {
 1158     }
 1159 
 1160     /**
 1161      * Constructs the query from the cached form.
 1162      *
 1163      * @param string  The cached query, in a serialized form.
 1164      * @return array  The custom component that was cached together with the essential
 1165      *                query data. This can be either a result set (result caching)
 1166      *                or an SQL query string (query caching).
 1167      */
 1168     protected function _constructQueryFromCache($cached)
 1169     {
 1170         $cached = unserialize($cached);
 1171         $this->_tableAliasMap = $cached[2];
 1172         $customComponent = $cached[0];
 1173 
 1174         $queryComponents = array();
 1175         $cachedComponents = $cached[1];
 1176         foreach ($cachedComponents as $alias => $components) {
 1177             $e = explode('.', $components['name']);
 1178             if (count($e) === 1) {
 1179                 $manager = Doctrine_Manager::getInstance(); 
 1180                 if ( ! $this->_passedConn && $manager->hasConnectionForComponent($e[0])) { 
 1181                     $this->_conn = $manager->getConnectionForComponent($e[0]); 
 1182                 }
 1183                 $queryComponents[$alias]['table'] = $this->_conn->getTable($e[0]);
 1184             } else {
 1185                 $queryComponents[$alias]['parent'] = $e[0];
 1186                 $queryComponents[$alias]['relation'] = $queryComponents[$e[0]]['table']->getRelation($e[1]);
 1187                 $queryComponents[$alias]['table'] = $queryComponents[$alias]['relation']->getTable();
 1188             }
 1189             if (isset($components['agg'])) {
 1190                 $queryComponents[$alias]['agg'] = $components['agg'];
 1191             }
 1192             if (isset($components['map'])) {
 1193                 $queryComponents[$alias]['map'] = $components['map'];
 1194             }
 1195         }
 1196         $this->_queryComponents = $queryComponents;
 1197 
 1198         return $customComponent;
 1199     }
 1200 
 1201     /**
 1202      * getCachedForm
 1203      * returns the cached form of this query for given resultSet
 1204      *
 1205      * @param array $resultSet
 1206      * @return string           serialized string representation of this query
 1207      */
 1208     public function getCachedForm($customComponent = null)
 1209     {
 1210         $componentInfo = array();
 1211 
 1212         foreach ($this->getQueryComponents() as $alias => $components) {
 1213             if ( ! isset($components['parent'])) {
 1214                 $componentInfo[$alias]['name'] = $components['table']->getComponentName();
 1215             } else {
 1216                 $componentInfo[$alias]['name'] = $components['parent'] . '.' . $components['relation']->getAlias();
 1217             }
 1218             if (isset($components['agg'])) {
 1219                 $componentInfo[$alias]['agg'] = $components['agg'];
 1220             }
 1221             if (isset($components['map'])) {
 1222                 $componentInfo[$alias]['map'] = $components['map'];
 1223             }
 1224         }
 1225 
 1226         if ($customComponent instanceof Doctrine_Collection) {
 1227             foreach ($customComponent as $record) {
 1228                 $record->serializeReferences(true);
 1229             }
 1230         }
 1231 
 1232         return serialize(array($customComponent, $componentInfo, $this->getTableAliasMap()));
 1233     }
 1234 
 1235     /**
 1236      * Adds fields or aliased functions.
 1237      *
 1238      * This method adds fields or dbms functions to the SELECT query part.
 1239      * <code>
 1240      * $query->addSelect('COUNT(p.id) as num_phonenumbers');
 1241      * </code>
 1242      *
 1243      * @param string $select        Query SELECT part
 1244      * @return Doctrine_Query
 1245      */
 1246     public function addSelect($select)
 1247     {
 1248         return $this->_addDqlQueryPart('select', $select, true);
 1249     }
 1250 
 1251     /**
 1252      * addSqlTableAlias
 1253      * adds an SQL table alias and associates it a component alias
 1254      *
 1255      * @param string $componentAlias    the alias for the query component associated with given tableAlias
 1256      * @param string $tableAlias        the table alias to be added
 1257      * @return Doctrine_Query_Abstract
 1258      */
 1259     public function addSqlTableAlias($sqlTableAlias, $componentAlias)
 1260     {
 1261         $this->_tableAliasMap[$sqlTableAlias] = $componentAlias;
 1262         return $this;
 1263     }
 1264 
 1265     /**
 1266      * addFrom
 1267      * adds fields to the FROM part of the query
 1268      *
 1269      * @param string $from        Query FROM part
 1270      * @return Doctrine_Query
 1271      */
 1272     public function addFrom($from)
 1273     {
 1274         return $this->_addDqlQueryPart('from', $from, true);
 1275     }
 1276 
 1277     /**
 1278      * Alias for @see andWhere().
 1279      * @return Doctrine_Query   this object
 1280      */
 1281     public function addWhere($where, $params = array())
 1282     {
 1283         return $this->andWhere($where, $params);
 1284     }
 1285 
 1286     /**
 1287      * Adds conditions to the WHERE part of the query.
 1288      * <code>
 1289      * $q->andWhere('u.birthDate > ?', '1975-01-01');
 1290      * </code>
 1291      *
 1292      * @param string $where Query WHERE part
 1293      * @param mixed $params An array of parameters or a simple scalar
 1294      * @return Doctrine_Query
 1295      */
 1296     public function andWhere($where, $params = array())
 1297     {
 1298         if (is_array($params)) {
 1299             $this->_params['where'] = array_merge($this->_params['where'], $params);
 1300         } else {
 1301             $this->_params['where'][] = $params;
 1302         }
 1303 
 1304         if ($this->_hasDqlQueryPart('where')) {
 1305             $this->_addDqlQueryPart('where', 'AND', true);
 1306         }
 1307 
 1308         return $this->_addDqlQueryPart('where', $where, true);
 1309     }
 1310 
 1311     /**
 1312      * Adds conditions to the WHERE part of the query
 1313      * <code>
 1314      * $q->orWhere('u.role = ?', 'admin');
 1315      * </code>
 1316      *
 1317      * @param string $where Query WHERE part
 1318      * @param mixed $params An array of parameters or a simple scalar
 1319      * @return Doctrine_Query
 1320      */
 1321     public function orWhere($where, $params = array())
 1322     {
 1323         if (is_array($params)) {
 1324             $this->_params['where'] = array_merge($this->_params['where'], $params);
 1325         } else {
 1326             $this->_params['where'][] = $params;
 1327         }
 1328 
 1329         if ($this->_hasDqlQueryPart('where')) {
 1330             $this->_addDqlQueryPart('where', 'OR', true);
 1331         }
 1332 
 1333         return $this->_addDqlQueryPart('where', $where, true);
 1334     }
 1335 
 1336     /**
 1337      * Adds IN condition to the query WHERE part. Alias to @see andWhereIn().
 1338      *
 1339      * @param string $expr          the operand of the IN
 1340      * @param mixed $params         an array of parameters or a simple scalar
 1341      * @param boolean $not          whether or not to use NOT in front of IN
 1342      * @return Doctrine_Query
 1343      */
 1344     public function whereIn($expr, $params = array(), $not = false)
 1345     {
 1346         return $this->andWhereIn($expr, $params, $not);
 1347     }
 1348 
 1349     /**
 1350      * Adds IN condition to the query WHERE part
 1351      * <code>
 1352      * $q->whereIn('u.id', array(10, 23, 44));
 1353      * </code>
 1354      *
 1355      * @param string $expr      The operand of the IN
 1356      * @param mixed $params     An array of parameters or a simple scalar
 1357      * @param boolean $not      Whether or not to use NOT in front of IN. Defaults to false (simple IN clause)
 1358      * @return Doctrine_Query   this object.
 1359      */
 1360     public function andWhereIn($expr, $params = array(), $not = false)
 1361     {
 1362         // if there's no params, return (else we'll get a WHERE IN (), invalid SQL)
 1363         if (isset($params) and (count($params) == 0)) {
 1364             return $this;
 1365         }
 1366 
 1367         if ($this->_hasDqlQueryPart('where')) {
 1368             $this->_addDqlQueryPart('where', 'AND', true);
 1369         }
 1370 
 1371         return $this->_addDqlQueryPart('where', $this->_processWhereIn($expr, $params, $not), true);
 1372     }
 1373 
 1374     /**
 1375      * Adds IN condition to the query WHERE part, appending it with an OR operator.
 1376      * <code>
 1377      * $q->orWhereIn('u.id', array(10, 23))
 1378      *   ->orWhereIn('u.id', 44);
 1379      * // will select all record with id equal to 10, 23 or 44
 1380      * </code>
 1381      *
 1382      * @param string $expr The operand of the IN
 1383      * @param mixed $params An array of parameters or a simple scalar
 1384      * @param boolean $not Whether or not to use NOT in front of IN
 1385      * @return Doctrine_Query
 1386      */
 1387     public function orWhereIn($expr, $params = array(), $not = false)
 1388     {
 1389         // if there's no params, return (else we'll get a WHERE IN (), invalid SQL)
 1390         if (isset($params) and (count($params) == 0)) {
 1391             return $this;
 1392         }
 1393 
 1394         if ($this->_hasDqlQueryPart('where')) {
 1395             $this->_addDqlQueryPart('where', 'OR', true);
 1396         }
 1397 
 1398         return $this->_addDqlQueryPart('where', $this->_processWhereIn($expr, $params, $not), true);
 1399     }
 1400 
 1401     /**
 1402      * @nodoc
 1403      */
 1404     protected function _processWhereIn($expr, $params = array(), $not = false)
 1405     {
 1406         $params = (array) $params;
 1407 
 1408         // if there's no params, return (else we'll get a WHERE IN (), invalid SQL)
 1409         if (count($params) == 0) {
 1410             throw new Doctrine_Query_Exception('You must pass at least one parameter when using an IN() condition.');
 1411         }
 1412 
 1413         $a = array();
 1414         foreach ($params as $k => $value) {
 1415             if ($value instanceof Doctrine_Expression) {
 1416                 $value = $value->getSql();
 1417                 unset($params[$k]);
 1418             } else {
 1419                 $value = '?';
 1420             }
 1421             $a[] = $value;
 1422         }
 1423 
 1424         $this->_params['where'] = array_merge($this->_params['where'], $params);
 1425 
 1426         return $expr . ($not === true ? ' NOT' : '') . ' IN (' . implode(', ', $a) . ')';
 1427     }
 1428 
 1429     /**
 1430      * Adds NOT IN condition to the query WHERE part.
 1431      * <code>
 1432      * $q->whereNotIn('u.id', array(10, 20));
 1433      * // will exclude users with id 10 and 20 from the select
 1434      * </code>
 1435      *
 1436      * @param string $expr          the operand of the NOT IN
 1437      * @param mixed $params         an array of parameters or a simple scalar
 1438      * @return Doctrine_Query       this object
 1439      */
 1440     public function whereNotIn($expr, $params = array())
 1441     {
 1442         return $this->whereIn($expr, $params, true);
 1443     }
 1444 
 1445     /**
 1446      * Adds NOT IN condition to the query WHERE part
 1447      * Alias for @see whereNotIn().
 1448      *
 1449      * @param string $expr The operand of the NOT IN
 1450      * @param mixed $params An array of parameters or a simple scalar
 1451      * @return Doctrine_Query
 1452      */
 1453     public function andWhereNotIn($expr, $params = array())
 1454     {
 1455         return $this->andWhereIn($expr, $params, true);
 1456     }
 1457 
 1458     /**
 1459      * Adds NOT IN condition to the query WHERE part
 1460      *
 1461      * @param string $expr The operand of the NOT IN
 1462      * @param mixed $params An array of parameters or a simple scalar
 1463      * @return Doctrine_Query
 1464      */
 1465     public function orWhereNotIn($expr, $params = array())
 1466     {
 1467         return $this->orWhereIn($expr, $params, true);
 1468     }
 1469 
 1470     /**
 1471      * Adds fields to the GROUP BY part of the query.
 1472      * <code>
 1473      * $q->groupBy('u.id');
 1474      * </code>
 1475      *
 1476      * @param string $groupby       Query GROUP BY part
 1477      * @return Doctrine_Query
 1478      */
 1479     public function addGroupBy($groupby)
 1480     {
 1481         return $this->_addDqlQueryPart('groupby', $groupby, true);
 1482     }
 1483 
 1484     /**
 1485      * Adds conditions to the HAVING part of the query.
 1486      *
 1487      * This methods add HAVING clauses. These clauses are used to narrow the 
 1488      * results by operating on aggregated values.
 1489      * <code>
 1490      * $q->having('num_phonenumbers > ?', 1);
 1491      * </code>
 1492      *
 1493      * @param string $having        Query HAVING part
 1494      * @param mixed $params         an array of parameters or a simple scalar
 1495      * @return Doctrine_Query
 1496      */
 1497     public function addHaving($having, $params = array())
 1498     {
 1499         if (is_array($params)) {
 1500             $this->_params['having'] = array_merge($this->_params['having'], $params);
 1501         } else {
 1502             $this->_params['having'][] = $params;
 1503         }
 1504         return $this->_addDqlQueryPart('having', $having, true);
 1505     }
 1506 
 1507     /**
 1508      * addOrderBy
 1509      * adds fields to the ORDER BY part of the query
 1510      *
 1511      * @param string $orderby       Query ORDER BY part
 1512      * @return Doctrine_Query
 1513      */
 1514     public function addOrderBy($orderby)
 1515     {
 1516         return $this->_addDqlQueryPart('orderby', $orderby, true);
 1517     }
 1518 
 1519     /**
 1520      * select
 1521      * sets the SELECT part of the query
 1522      *
 1523      * @param string $select        Query SELECT part
 1524      * @return Doctrine_Query
 1525      */
 1526     public function select($select = null)
 1527     {
 1528         $this->_type = self::SELECT;
 1529         if ($select) {
 1530             return $this->_addDqlQueryPart('select', $select);
 1531         } else {
 1532             return $this;
 1533         }
 1534     }
 1535 
 1536     /**
 1537      * distinct
 1538      * Makes the query SELECT DISTINCT.
 1539      * <code>
 1540      * $q->distinct();
 1541      * </code>
 1542      *
 1543      * @param bool $flag            Whether or not the SELECT is DISTINCT (default true).
 1544      * @return Doctrine_Query
 1545      */
 1546     public function distinct($flag = true)
 1547     {
 1548         $this->_sqlParts['distinct'] = (bool) $flag;
 1549         return $this;
 1550     }
 1551 
 1552     /**
 1553      * forUpdate
 1554      * Makes the query SELECT FOR UPDATE.
 1555      *
 1556      * @param bool $flag            Whether or not the SELECT is FOR UPDATE (default true).
 1557      * @return Doctrine_Query
 1558      */
 1559     public function forUpdate($flag = true)
 1560     {
 1561         $this->_sqlParts['forUpdate'] = (bool) $flag;
 1562         return $this;
 1563     }
 1564 
 1565     /**
 1566      * delete
 1567      * sets the query type to DELETE
 1568      *
 1569      * @return Doctrine_Query
 1570      */
 1571     public function delete($from = null)
 1572     {
 1573         $this->_type = self::DELETE;
 1574         if ($from != null) {
 1575             return $this->_addDqlQueryPart('from', $from);
 1576         }
 1577         return $this;
 1578     }
 1579 
 1580     /**
 1581      * update
 1582      * sets the UPDATE part of the query
 1583      *
 1584      * @param string $update        Query UPDATE part
 1585      * @return Doctrine_Query
 1586      */
 1587     public function update($from = null)
 1588     {
 1589         $this->_type = self::UPDATE;
 1590         if ($from != null) {
 1591             return $this->_addDqlQueryPart('from', $from);
 1592         }
 1593         return $this;
 1594     }
 1595 
 1596     /**
 1597      * set
 1598      * sets the SET part of the query
 1599      *
 1600      * @param string $update        Query UPDATE part
 1601      * @return Doctrine_Query
 1602      */
 1603     public function set($key, $value = null, $params = null)
 1604     {
 1605         if (is_array($key)) {
 1606             foreach ($key as $k => $v) {
 1607                 $this->set($k, '?', array($v));
 1608             }
 1609             return $this;
 1610         } else {
 1611             if ($params !== null) {
 1612                 if (is_array($params)) {
 1613                     $this->_params['set'] = array_merge($this->_params['set'], $params);
 1614                 } else {
 1615                     $this->_params['set'][] = $params;
 1616                 }
 1617             }
 1618 
 1619             return $this->_addDqlQueryPart('set', $key . ' = ' . $value, true);
 1620         }
 1621     }
 1622 
 1623     /**
 1624      * from
 1625      * sets the FROM part of the query
 1626      * <code>
 1627      * $q->from('User u');
 1628      * </code>
 1629      *
 1630      * @param string $from          Query FROM part
 1631      * @return Doctrine_Query
 1632      */
 1633     public function from($from)
 1634     {
 1635         return $this->_addDqlQueryPart('from', $from);
 1636     }
 1637 
 1638     /**
 1639      * innerJoin
 1640      * appends an INNER JOIN to the FROM part of the query
 1641      *
 1642      * @param string $join         Query INNER JOIN
 1643      * @return Doctrine_Query
 1644      */
 1645     public function innerJoin($join, $params = array())
 1646     {
 1647         if (is_array($params)) {
 1648             $this->_params['join'] = array_merge($this->_params['join'], $params);
 1649         } else {
 1650             $this->_params['join'][] = $params;
 1651         }
 1652 
 1653         return $this->_addDqlQueryPart('from', 'INNER JOIN ' . $join, true);
 1654     }
 1655 
 1656     /**
 1657      * leftJoin
 1658      * appends a LEFT JOIN to the FROM part of the query
 1659      *
 1660      * @param string $join         Query LEFT JOIN
 1661      * @return Doctrine_Query
 1662      */
 1663     public function leftJoin($join, $params = array())
 1664     {
 1665         if (is_array($params)) {
 1666             $this->_params['join'] = array_merge($this->_params['join'], $params);
 1667         } else {
 1668             $this->_params['join'][] = $params;
 1669         }
 1670 
 1671         return $this->_addDqlQueryPart('from', 'LEFT JOIN ' . $join, true);
 1672     }
 1673 
 1674     /**
 1675      * groupBy
 1676      * sets the GROUP BY part of the query
 1677      *
 1678      * @param string $groupby      Query GROUP BY part
 1679      * @return Doctrine_Query
 1680      */
 1681     public function groupBy($groupby)
 1682     {
 1683         return $this->_addDqlQueryPart('groupby', $groupby);
 1684     }
 1685 
 1686     /**
 1687      * where
 1688      * sets the WHERE part of the query
 1689      *
 1690      * @param string $join         Query WHERE part
 1691      * @param mixed $params        an array of parameters or a simple scalar
 1692      * @return Doctrine_Query
 1693      */
 1694     public function where($where, $params = array())
 1695     {
 1696         $this->_params['where'] = array();
 1697 
 1698         if (is_array($params)) {
 1699             $this->_params['where'] = $params;
 1700         } else {
 1701             $this->_params['where'][] = $params;
 1702         }
 1703 
 1704         return $this->_addDqlQueryPart('where', $where);
 1705     }
 1706 
 1707     /**
 1708      * having
 1709      * sets the HAVING part of the query
 1710      *
 1711      * @param string $having       Query HAVING part
 1712      * @param mixed $params        an array of parameters or a simple scalar
 1713      * @return Doctrine_Query
 1714      */
 1715     public function having($having, $params = array())
 1716     {
 1717         $this->_params['having'] = array();
 1718         if (is_array($params)) {
 1719             $this->_params['having'] = $params;
 1720         } else {
 1721             $this->_params['having'][] = $params;
 1722         }
 1723 
 1724         return $this->_addDqlQueryPart('having', $having);
 1725     }
 1726 
 1727     /**
 1728      * Sets the ORDER BY part of the query.
 1729      * <code>
 1730      * $q->orderBy('u.name');
 1731      * $query->orderBy('u.birthDate DESC');
 1732      * </code>
 1733      *
 1734      * @param string $orderby      Query ORDER BY part
 1735      * @return Doctrine_Query
 1736      */
 1737     public function orderBy($orderby)
 1738     {
 1739         return $this->_addDqlQueryPart('orderby', $orderby);
 1740     }
 1741 
 1742     /**
 1743      * limit
 1744      * sets the Query query limit
 1745      *
 1746      * @param integer $limit        limit to be used for limiting the query results
 1747      * @return Doctrine_Query
 1748      */
 1749     public function limit($limit)
 1750     {
 1751         return $this->_addDqlQueryPart('limit', $limit);
 1752     }
 1753 
 1754     /**
 1755      * offset
 1756      * sets the Query query offset
 1757      *
 1758      * @param integer $offset       offset to be used for paginating the query
 1759      * @return Doctrine_Query
 1760      */
 1761     public function offset($offset)
 1762     {
 1763         return $this->_addDqlQueryPart('offset', $offset);
 1764     }
 1765 
 1766     /**
 1767      * Resets all the sql parts.
 1768      *
 1769      * @return void
 1770      */
 1771     protected function clear()
 1772     {
 1773         $this->_sqlParts = array(
 1774                     'select'    => array(),
 1775                     'distinct'  => false,
 1776                     'forUpdate' => false,
 1777                     'from'      => array(),
 1778                     'set'       => array(),
 1779                     'join'      => array(),
 1780                     'where'     => array(),
 1781                     'groupby'   => array(),
 1782                     'having'    => array(),
 1783                     'orderby'   => array(),
 1784                     'limit'     => false,
 1785                     'offset'    => false,
 1786                     );
 1787     }
 1788 
 1789     public function setHydrationMode($hydrationMode)
 1790     {
 1791         $this->_hydrator->setHydrationMode($hydrationMode);
 1792         return $this;
 1793     }
 1794 
 1795     /**
 1796      * Gets the components of this query.
 1797      */
 1798     public function getQueryComponents()
 1799     {
 1800         return $this->_queryComponents;
 1801     }
 1802 
 1803     /**
 1804      * Return the SQL parts.
 1805      *
 1806      * @return array The parts
 1807      */
 1808     public function getSqlParts()
 1809     {
 1810         return $this->_sqlParts;
 1811     }
 1812 
 1813     /**
 1814      * getType
 1815      *
 1816      * returns the type of this query object
 1817      * by default the type is Doctrine_Query_Abstract::SELECT but if update() or delete()
 1818      * are being called the type is Doctrine_Query_Abstract::UPDATE and Doctrine_Query_Abstract::DELETE,
 1819      * respectively
 1820      *
 1821      * @see Doctrine_Query_Abstract::SELECT
 1822      * @see Doctrine_Query_Abstract::UPDATE
 1823      * @see Doctrine_Query_Abstract::DELETE
 1824      *
 1825      * @return integer      return the query type
 1826      */
 1827     public function getType()
 1828     {
 1829         return $this->_type;
 1830     }
 1831 
 1832     /**
 1833      * useResultCache
 1834      *
 1835      * @param Doctrine_Cache_Interface|bool $driver      cache driver
 1836      * @param integer $timeToLive                        how long the cache entry is valid
 1837      * @param string $resultCacheHash                     The key to use for storing the queries result cache entry
 1838      * @return Doctrine_Query         this object
 1839      */
 1840     public function useResultCache($driver = true, $timeToLive = null, $resultCacheHash = null)
 1841     {
 1842         if ($driver !== null && $driver !== true && ! ($driver instanceOf Doctrine_Cache_Interface)) {
 1843             $msg = 'First argument should be instance of Doctrine_Cache_Interface or null.';
 1844             throw new Doctrine_Query_Exception($msg);
 1845         }
 1846         $this->_resultCache = $driver;
 1847         $this->_resultCacheHash = $resultCacheHash;
 1848 
 1849         if ($timeToLive !== null) {
 1850             $this->setResultCacheLifeSpan($timeToLive);
 1851         }
 1852         return $this;
 1853     }
 1854 
 1855     /**
 1856      * Set the result cache hash to be used for storing the results in the cache driver
 1857      *
 1858      * @param string $resultCacheHash
 1859      * @return void
 1860      */
 1861     public function setResultCacheHash($resultCacheHash)
 1862     {
 1863         $this->_resultCacheHash = $resultCacheHash;
 1864 
 1865         return $this;
 1866     }
 1867 
 1868     /**
 1869      * Clear the result cache entry for this query
 1870      *
 1871      * @return void
 1872      */
 1873     public function clearResultCache()
 1874     {
 1875         $this->getResultCacheDriver()
 1876             ->delete($this->getResultCacheHash());
 1877 
 1878         return $this;
 1879     }
 1880 
 1881     /**
 1882      * useQueryCache
 1883      *
 1884      * @param Doctrine_Cache_Interface|bool $driver      cache driver
 1885      * @param integer $timeToLive                        how long the cache entry is valid
 1886      * @return Doctrine_Query         this object
 1887      */
 1888     public function useQueryCache($driver = true, $timeToLive = null)
 1889     {
 1890         if ($driver !== null && $driver !== true && $driver !== false && ! ($driver instanceOf Doctrine_Cache_Interface)) {
 1891             $msg = 'First argument should be instance of Doctrine_Cache_Interface or null.';
 1892             throw new Doctrine_Query_Exception($msg);
 1893         }
 1894         $this->_queryCache = $driver;
 1895 
 1896         if ($timeToLive !== null) {
 1897             $this->setQueryCacheLifeSpan($timeToLive);
 1898         }
 1899         return $this;
 1900     }
 1901 
 1902     /**
 1903      * expireCache
 1904      *
 1905      * @param boolean $expire       whether or not to force cache expiration
 1906      * @return Doctrine_Query     this object
 1907      */
 1908     public function expireResultCache($expire = true)
 1909     {
 1910         $this->_expireResultCache = $expire;
 1911         return $this;
 1912     }
 1913 
 1914     /**
 1915      * expireQueryCache
 1916      *
 1917      * @param boolean $expire       whether or not to force cache expiration
 1918      * @return Doctrine_Query     this object
 1919      */
 1920     public function expireQueryCache($expire = true)
 1921     {
 1922         $this->_expireQueryCache = $expire;
 1923         return $this;
 1924     }
 1925 
 1926     /**
 1927      * setResultCacheLifeSpan
 1928      *
 1929      * @param integer $timeToLive   how long the cache entry is valid (in seconds)
 1930      * @return Doctrine_Query     this object
 1931      */
 1932     public function setResultCacheLifeSpan($timeToLive)
 1933     {
 1934         if ($timeToLive !== null) {
 1935             $timeToLive = (int) $timeToLive;
 1936         }
 1937         $this->_resultCacheTTL = $timeToLive;
 1938 
 1939         return $this;
 1940     }
 1941 
 1942     /**
 1943      * Gets the life span of the result cache in seconds.
 1944      *
 1945      * @return integer
 1946      */
 1947     public function getResultCacheLifeSpan()
 1948     {
 1949         return $this->_resultCacheTTL;
 1950     }
 1951 
 1952     /**
 1953      * setQueryCacheLifeSpan
 1954      *
 1955      * @param integer $timeToLive   how long the cache entry is valid
 1956      * @return Doctrine_Query     this object
 1957      */
 1958     public function setQueryCacheLifeSpan($timeToLive)
 1959     {
 1960         if ($timeToLive !== null) {
 1961             $timeToLive = (int) $timeToLive;
 1962         }
 1963         $this->_queryCacheTTL = $timeToLive;
 1964 
 1965         return $this;
 1966     }
 1967 
 1968     /**
 1969      * Gets the life span of the query cache the Query object is using.
 1970      *
 1971      * @return integer  The life span in seconds.
 1972      */
 1973     public function getQueryCacheLifeSpan()
 1974     {
 1975         return $this->_queryCacheTTL;
 1976     }
 1977 
 1978     /**
 1979      * getResultCacheDriver
 1980      * returns the cache driver used for caching result sets
 1981      *
 1982      * @return Doctrine_Cache_Interface|boolean|null    cache driver
 1983      */
 1984     public function getResultCacheDriver()
 1985     {
 1986         if ($this->_resultCache instanceof Doctrine_Cache_Interface) {
 1987             return $this->_resultCache;
 1988         } else {
 1989             return $this->_conn->getResultCacheDriver();
 1990         }
 1991     }
 1992 
 1993     /**
 1994      * getQueryCacheDriver
 1995      * returns the cache driver used for caching queries
 1996      *
 1997      * @return Doctrine_Cache_Interface|boolean|null    cache driver
 1998      */
 1999     public function getQueryCacheDriver()
 2000     {
 2001         if ($this->_queryCache instanceof Doctrine_Cache_Interface) {
 2002             return $this->_queryCache;
 2003         } else {
 2004             return $this->_conn->getQueryCacheDriver();
 2005         }
 2006     }
 2007 
 2008     /**
 2009      * getConnection
 2010      *
 2011      * @return Doctrine_Connection
 2012      */
 2013     public function getConnection()
 2014     {
 2015         return $this->_conn;
 2016     }
 2017 
 2018     /**
 2019      * Checks if there's at least one DQL part defined to the internal parts collection.
 2020      *
 2021      * @param string $queryPartName  The name of the query part.
 2022      * @return boolean
 2023      */
 2024     protected function _hasDqlQueryPart($queryPartName)
 2025     {
 2026         return count($this->_dqlParts[$queryPartName]) > 0;
 2027     }
 2028 
 2029     /**
 2030      * Adds a DQL part to the internal parts collection.
 2031      *
 2032      * This method add the part specified to the array named by $queryPartName.
 2033      * Most part names support multiple parts addition.
 2034      *
 2035      * @see $_dqlParts;
 2036      * @see Doctrine_Query::getDqlPart()
 2037      * @param string $queryPartName  The name of the query part.
 2038      * @param string $queryPart      The actual query part to add.
 2039      * @param boolean $append        Whether to append $queryPart to already existing
 2040      *                               parts under the same $queryPartName. Defaults to FALSE
 2041      *                               (previously added parts with the same name get overridden).
 2042      */
 2043     protected function _addDqlQueryPart($queryPartName, $queryPart, $append = false)
 2044     {
 2045         // We should prevent nullable query parts
 2046         if ($queryPart === null) {
 2047             throw new Doctrine_Query_Exception('Cannot define NULL as part of query when defining \'' . $queryPartName . '\'.');
 2048         }
 2049 
 2050         if ($append) {
 2051             $this->_dqlParts[$queryPartName][] = $queryPart;
 2052         } else {
 2053             $this->_dqlParts[$queryPartName] = array($queryPart);
 2054         }
 2055 
 2056         $this->_state = Doctrine_Query::STATE_DIRTY;
 2057         return $this;
 2058     }
 2059 
 2060     /**
 2061      * _processDqlQueryPart
 2062      * parses given query part
 2063      *
 2064      * @param string $queryPartName     the name of the query part
 2065      * @param array $queryParts         an array containing the query part data
 2066      * @return Doctrine_Query           this object
 2067      * @todo Better description. "parses given query part" ??? Then wheres the difference
 2068      *       between process/parseQueryPart? I suppose this does something different.
 2069      */
 2070     protected function _processDqlQueryPart($queryPartName, $queryParts)
 2071     {
 2072         $this->removeSqlQueryPart($queryPartName);
 2073 
 2074         if (is_array($queryParts) && ! empty($queryParts)) {
 2075             foreach ($queryParts as $queryPart) {
 2076                 $parser = $this->_getParser($queryPartName);
 2077                 $sql = $parser->parse($queryPart);
 2078                 if (isset($sql)) {
 2079                     if ($queryPartName == 'limit' || $queryPartName == 'offset') {
 2080                         $this->setSqlQueryPart($queryPartName, $sql);
 2081                     } else {
 2082                         $this->addSqlQueryPart($queryPartName, $sql);
 2083                     }
 2084                 }
 2085             }
 2086         }
 2087     }
 2088 
 2089     /**
 2090      * _getParser
 2091      * parser lazy-loader
 2092      *
 2093      * @throws Doctrine_Query_Exception     if unknown parser name given
 2094      * @return Doctrine_Query_Part
 2095      * @todo Doc/Description: What is the parameter for? Which parsers are available?
 2096      */
 2097     protected function _getParser($name)
 2098     {
 2099         if ( ! isset($this->_parsers[$name])) {
 2100             $class = 'Doctrine_Query_' . ucwords(strtolower($name));
 2101 
 2102             Doctrine_Core::autoload($class);
 2103 
 2104             if ( ! class_exists($class)) {
 2105                 throw new Doctrine_Query_Exception('Unknown parser ' . $name);
 2106             }
 2107 
 2108             $this->_parsers[$name] = new $class($this, $this->_tokenizer);
 2109         }
 2110 
 2111         return $this->_parsers[$name];
 2112     }
 2113 
 2114     /**
 2115      * Gets the SQL query that corresponds to this query object.
 2116      * The returned SQL syntax depends on the connection driver that is used
 2117      * by this query object at the time of this method call.
 2118      *
 2119      * @param array $params
 2120      */
 2121     abstract public function getSqlQuery($params = array());
 2122 
 2123     /**
 2124      * parseDqlQuery
 2125      * parses a dql query
 2126      *
 2127      * @param string $query         query to be parsed
 2128      * @return Doctrine_Query_Abstract  this object
 2129      */
 2130     abstract public function parseDqlQuery($query);
 2131 
 2132     /**
 2133      * toString magic call
 2134      * this method is automatically called when Doctrine_Query object is trying to be used as a string
 2135      * So, it it converted into its DQL correspondant
 2136      *
 2137      * @return string DQL string
 2138      */
 2139     public function __toString()
 2140     {
 2141         return $this->getDql();
 2142     }
 2143 }