source: pro-violet-viettel/sourcecode/application/libraries/Doctrine/ORM/Query/Parser.php @ 353

Last change on this file since 353 was 345, checked in by quyenla, 11 years ago

collaborator page

File size: 99.0 KB
Line 
1<?php
2/*
3 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHARNTABILITY AND FITNESS FOR
6 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14 *
15 * This software consists of voluntary contributions made by many individuals
16 * and is licensed under the LGPL. For more information, see
17 * <http://www.doctrine-project.org>.
18 */
19
20namespace Doctrine\ORM\Query;
21
22use Doctrine\ORM\Query;
23use Doctrine\ORM\Mapping\ClassMetadata;
24
25/**
26 * An LL(*) recursive-descent parser for the context-free grammar of the Doctrine Query Language.
27 * Parses a DQL query, reports any errors in it, and generates an AST.
28 *
29 * @since   2.0
30 * @author  Guilherme Blanco <guilhermeblanco@hotmail.com>
31 * @author  Jonathan Wage <jonwage@gmail.com>
32 * @author  Roman Borschel <roman@code-factory.org>
33 * @author  Janne Vanhala <jpvanhal@cc.hut.fi>
34 */
35class Parser
36{
37    /** READ-ONLY: Maps BUILT-IN string function names to AST class names. */
38    private static $_STRING_FUNCTIONS = array(
39        'concat'    => 'Doctrine\ORM\Query\AST\Functions\ConcatFunction',
40        'substring' => 'Doctrine\ORM\Query\AST\Functions\SubstringFunction',
41        'trim'      => 'Doctrine\ORM\Query\AST\Functions\TrimFunction',
42        'lower'     => 'Doctrine\ORM\Query\AST\Functions\LowerFunction',
43        'upper'     => 'Doctrine\ORM\Query\AST\Functions\UpperFunction',
44        'identity'  => 'Doctrine\ORM\Query\AST\Functions\IdentityFunction',
45    );
46
47    /** READ-ONLY: Maps BUILT-IN numeric function names to AST class names. */
48    private static $_NUMERIC_FUNCTIONS = array(
49        'length'    => 'Doctrine\ORM\Query\AST\Functions\LengthFunction',
50        'locate'    => 'Doctrine\ORM\Query\AST\Functions\LocateFunction',
51        'abs'       => 'Doctrine\ORM\Query\AST\Functions\AbsFunction',
52        'sqrt'      => 'Doctrine\ORM\Query\AST\Functions\SqrtFunction',
53        'mod'       => 'Doctrine\ORM\Query\AST\Functions\ModFunction',
54        'size'      => 'Doctrine\ORM\Query\AST\Functions\SizeFunction',
55        'date_diff' => 'Doctrine\ORM\Query\AST\Functions\DateDiffFunction',
56        'bit_and'   => 'Doctrine\ORM\Query\AST\Functions\BitAndFunction',
57        'bit_or'    => 'Doctrine\ORM\Query\AST\Functions\BitOrFunction',
58    );
59
60    /** READ-ONLY: Maps BUILT-IN datetime function names to AST class names. */
61    private static $_DATETIME_FUNCTIONS = array(
62        'current_date'      => 'Doctrine\ORM\Query\AST\Functions\CurrentDateFunction',
63        'current_time'      => 'Doctrine\ORM\Query\AST\Functions\CurrentTimeFunction',
64        'current_timestamp' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimestampFunction',
65        'date_add'          => 'Doctrine\ORM\Query\AST\Functions\DateAddFunction',
66        'date_sub'          => 'Doctrine\ORM\Query\AST\Functions\DateSubFunction',
67    );
68
69    /**
70     * Expressions that were encountered during parsing of identifiers and expressions
71     * and still need to be validated.
72     */
73    private $_deferredIdentificationVariables = array();
74    private $_deferredPartialObjectExpressions = array();
75    private $_deferredPathExpressions = array();
76    private $_deferredResultVariables = array();
77
78    /**
79     * The lexer.
80     *
81     * @var \Doctrine\ORM\Query\Lexer
82     */
83    private $_lexer;
84
85    /**
86     * The parser result.
87     *
88     * @var \Doctrine\ORM\Query\ParserResult
89     */
90    private $_parserResult;
91
92    /**
93     * The EntityManager.
94     *
95     * @var EnityManager
96     */
97    private $_em;
98
99    /**
100     * The Query to parse.
101     *
102     * @var Query
103     */
104    private $_query;
105
106    /**
107     * Map of declared query components in the parsed query.
108     *
109     * @var array
110     */
111    private $_queryComponents = array();
112
113    /**
114     * Keeps the nesting level of defined ResultVariables
115     *
116     * @var integer
117     */
118    private $_nestingLevel = 0;
119
120    /**
121     * Any additional custom tree walkers that modify the AST.
122     *
123     * @var array
124     */
125    private $_customTreeWalkers = array();
126
127    /**
128     * The custom last tree walker, if any, that is responsible for producing the output.
129     *
130     * @var TreeWalker
131     */
132    private $_customOutputWalker;
133
134    /**
135     * @var array
136     */
137    private $_identVariableExpressions = array();
138
139    /**
140     * Creates a new query parser object.
141     *
142     * @param Query $query The Query to parse.
143     */
144    public function __construct(Query $query)
145    {
146        $this->_query        = $query;
147        $this->_em           = $query->getEntityManager();
148        $this->_lexer        = new Lexer($query->getDql());
149        $this->_parserResult = new ParserResult();
150    }
151
152    /**
153     * Sets a custom tree walker that produces output.
154     * This tree walker will be run last over the AST, after any other walkers.
155     *
156     * @param string $className
157     */
158    public function setCustomOutputTreeWalker($className)
159    {
160        $this->_customOutputWalker = $className;
161    }
162
163    /**
164     * Adds a custom tree walker for modifying the AST.
165     *
166     * @param string $className
167     */
168    public function addCustomTreeWalker($className)
169    {
170        $this->_customTreeWalkers[] = $className;
171    }
172
173    /**
174     * Gets the lexer used by the parser.
175     *
176     * @return \Doctrine\ORM\Query\Lexer
177     */
178    public function getLexer()
179    {
180        return $this->_lexer;
181    }
182
183    /**
184     * Gets the ParserResult that is being filled with information during parsing.
185     *
186     * @return \Doctrine\ORM\Query\ParserResult
187     */
188    public function getParserResult()
189    {
190        return $this->_parserResult;
191    }
192
193    /**
194     * Gets the EntityManager used by the parser.
195     *
196     * @return EntityManager
197     */
198    public function getEntityManager()
199    {
200        return $this->_em;
201    }
202
203    /**
204     * Parse and build AST for the given Query.
205     *
206     * @return \Doctrine\ORM\Query\AST\SelectStatement |
207     *         \Doctrine\ORM\Query\AST\UpdateStatement |
208     *         \Doctrine\ORM\Query\AST\DeleteStatement
209     */
210    public function getAST()
211    {
212        // Parse & build AST
213        $AST = $this->QueryLanguage();
214
215        // Process any deferred validations of some nodes in the AST.
216        // This also allows post-processing of the AST for modification purposes.
217        $this->_processDeferredIdentificationVariables();
218
219        if ($this->_deferredPartialObjectExpressions) {
220            $this->_processDeferredPartialObjectExpressions();
221        }
222
223        if ($this->_deferredPathExpressions) {
224            $this->_processDeferredPathExpressions($AST);
225        }
226
227        if ($this->_deferredResultVariables) {
228            $this->_processDeferredResultVariables();
229        }
230
231        $this->_processRootEntityAliasSelected();
232
233        // TODO: Is there a way to remove this? It may impact the mixed hydration resultset a lot!
234        $this->fixIdentificationVariableOrder($AST);
235
236        return $AST;
237    }
238
239    /**
240     * Attempts to match the given token with the current lookahead token.
241     *
242     * If they match, updates the lookahead token; otherwise raises a syntax
243     * error.
244     *
245     * @param int token type
246     * @return void
247     * @throws QueryException If the tokens dont match.
248     */
249    public function match($token)
250    {
251        $lookaheadType = $this->_lexer->lookahead['type'];
252
253        // short-circuit on first condition, usually types match
254        if ($lookaheadType !== $token && $token !== Lexer::T_IDENTIFIER && $lookaheadType <= Lexer::T_IDENTIFIER) {
255            $this->syntaxError($this->_lexer->getLiteral($token));
256        }
257
258        $this->_lexer->moveNext();
259    }
260
261    /**
262     * Free this parser enabling it to be reused
263     *
264     * @param boolean $deep     Whether to clean peek and reset errors
265     * @param integer $position Position to reset
266     */
267    public function free($deep = false, $position = 0)
268    {
269        // WARNING! Use this method with care. It resets the scanner!
270        $this->_lexer->resetPosition($position);
271
272        // Deep = true cleans peek and also any previously defined errors
273        if ($deep) {
274            $this->_lexer->resetPeek();
275        }
276
277        $this->_lexer->token = null;
278        $this->_lexer->lookahead = null;
279    }
280
281    /**
282     * Parses a query string.
283     *
284     * @return ParserResult
285     */
286    public function parse()
287    {
288        $AST = $this->getAST();
289
290        if (($customWalkers = $this->_query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) !== false) {
291            $this->_customTreeWalkers = $customWalkers;
292        }
293
294        if (($customOutputWalker = $this->_query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER)) !== false) {
295            $this->_customOutputWalker = $customOutputWalker;
296        }
297
298        // Run any custom tree walkers over the AST
299        if ($this->_customTreeWalkers) {
300            $treeWalkerChain = new TreeWalkerChain($this->_query, $this->_parserResult, $this->_queryComponents);
301
302            foreach ($this->_customTreeWalkers as $walker) {
303                $treeWalkerChain->addTreeWalker($walker);
304            }
305
306            switch (true) {
307                case ($AST instanceof AST\UpdateStatement):
308                    $treeWalkerChain->walkUpdateStatement($AST);
309                    break;
310
311                case ($AST instanceof AST\DeleteStatement):
312                    $treeWalkerChain->walkDeleteStatement($AST);
313                    break;
314
315                case ($AST instanceof AST\SelectStatement):
316                default:
317                    $treeWalkerChain->walkSelectStatement($AST);
318            }
319        }
320
321        $outputWalkerClass = $this->_customOutputWalker ?: __NAMESPACE__ . '\SqlWalker';
322        $outputWalker      = new $outputWalkerClass($this->_query, $this->_parserResult, $this->_queryComponents);
323
324        // Assign an SQL executor to the parser result
325        $this->_parserResult->setSqlExecutor($outputWalker->getExecutor($AST));
326
327        return $this->_parserResult;
328    }
329
330    /**
331     * Fix order of identification variables.
332     *
333     * They have to appear in the select clause in the same order as the
334     * declarations (from ... x join ... y join ... z ...) appear in the query
335     * as the hydration process relies on that order for proper operation.
336     *
337     * @param AST\SelectStatement|AST\DeleteStatement|AST\UpdateStatement $AST
338     * @return void
339     */
340    private function fixIdentificationVariableOrder($AST)
341    {
342        if (count($this->_identVariableExpressions) <= 1) {
343            return;
344        }
345
346        foreach ($this->_queryComponents as $dqlAlias => $qComp) {
347            if ( ! isset($this->_identVariableExpressions[$dqlAlias])) {
348                continue;
349            }
350
351            $expr = $this->_identVariableExpressions[$dqlAlias];
352            $key  = array_search($expr, $AST->selectClause->selectExpressions);
353
354            unset($AST->selectClause->selectExpressions[$key]);
355
356            $AST->selectClause->selectExpressions[] = $expr;
357        }
358    }
359
360    /**
361     * Generates a new syntax error.
362     *
363     * @param string $expected Expected string.
364     * @param array $token Got token.
365     *
366     * @throws \Doctrine\ORM\Query\QueryException
367     */
368    public function syntaxError($expected = '', $token = null)
369    {
370        if ($token === null) {
371            $token = $this->_lexer->lookahead;
372        }
373
374        $tokenPos = (isset($token['position'])) ? $token['position'] : '-1';
375
376        $message  = "line 0, col {$tokenPos}: Error: ";
377        $message .= ($expected !== '') ? "Expected {$expected}, got " : 'Unexpected ';
378        $message .= ($this->_lexer->lookahead === null) ? 'end of string.' : "'{$token['value']}'";
379
380        throw QueryException::syntaxError($message);
381    }
382
383    /**
384     * Generates a new semantical error.
385     *
386     * @param string $message Optional message.
387     * @param array $token Optional token.
388     *
389     * @throws \Doctrine\ORM\Query\QueryException
390     */
391    public function semanticalError($message = '', $token = null)
392    {
393        if ($token === null) {
394            $token = $this->_lexer->lookahead;
395        }
396
397        // Minimum exposed chars ahead of token
398        $distance = 12;
399
400        // Find a position of a final word to display in error string
401        $dql    = $this->_query->getDql();
402        $length = strlen($dql);
403        $pos    = $token['position'] + $distance;
404        $pos    = strpos($dql, ' ', ($length > $pos) ? $pos : $length);
405        $length = ($pos !== false) ? $pos - $token['position'] : $distance;
406
407        $tokenPos = (isset($token['position']) && $token['position'] > 0) ? $token['position'] : '-1';
408        $tokenStr = substr($dql, $token['position'], $length);
409
410        // Building informative message
411        $message = 'line 0, col ' . $tokenPos . " near '" . $tokenStr . "': Error: " . $message;
412
413        throw QueryException::semanticalError($message);
414    }
415
416    /**
417     * Peeks beyond the specified token and returns the first token after that one.
418     *
419     * @param array $token
420     * @return array
421     */
422    private function _peekBeyond($token)
423    {
424        $peek = $this->_lexer->peek();
425
426        while ($peek['value'] != $token) {
427            $peek = $this->_lexer->peek();
428        }
429
430        $peek = $this->_lexer->peek();
431        $this->_lexer->resetPeek();
432
433        return $peek;
434    }
435
436    /**
437     * Peek beyond the matched closing parenthesis and return the first token after that one.
438     *
439     * @return array
440     */
441    private function _peekBeyondClosingParenthesis()
442    {
443        $token = $this->_lexer->peek();
444        $numUnmatched = 1;
445
446        while ($numUnmatched > 0 && $token !== null) {
447            switch ($token['type']) {
448                case Lexer::T_OPEN_PARENTHESIS:
449                    ++$numUnmatched;
450                    break;
451
452                case Lexer::T_CLOSE_PARENTHESIS:
453                    --$numUnmatched;
454                    break;
455
456                default:
457                    // Do nothing
458            }
459
460            $token = $this->_lexer->peek();
461        }
462
463        $this->_lexer->resetPeek();
464
465        return $token;
466    }
467
468    /**
469     * Checks if the given token indicates a mathematical operator.
470     *
471     * @return boolean TRUE if the token is a mathematical operator, FALSE otherwise.
472     */
473    private function _isMathOperator($token)
474    {
475        return in_array($token['type'], array(Lexer::T_PLUS, Lexer::T_MINUS, Lexer::T_DIVIDE, Lexer::T_MULTIPLY));
476    }
477
478    /**
479     * Checks if the next-next (after lookahead) token starts a function.
480     *
481     * @return boolean TRUE if the next-next tokens start a function, FALSE otherwise.
482     */
483    private function _isFunction()
484    {
485        $peek     = $this->_lexer->peek();
486        $nextpeek = $this->_lexer->peek();
487
488        $this->_lexer->resetPeek();
489
490        // We deny the COUNT(SELECT * FROM User u) here. COUNT won't be considered a function
491        return ($peek['type'] === Lexer::T_OPEN_PARENTHESIS && $nextpeek['type'] !== Lexer::T_SELECT);
492    }
493
494    /**
495     * Checks whether the given token type indicates an aggregate function.
496     *
497     * @return boolean TRUE if the token type is an aggregate function, FALSE otherwise.
498     */
499    private function _isAggregateFunction($tokenType)
500    {
501        return in_array($tokenType, array(Lexer::T_AVG, Lexer::T_MIN, Lexer::T_MAX, Lexer::T_SUM, Lexer::T_COUNT));
502    }
503
504    /**
505     * Checks whether the current lookahead token of the lexer has the type T_ALL, T_ANY or T_SOME.
506     *
507     * @return boolean
508     */
509    private function _isNextAllAnySome()
510    {
511        return in_array($this->_lexer->lookahead['type'], array(Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME));
512    }
513
514    /**
515     * Validates that the given <tt>IdentificationVariable</tt> is semantically correct.
516     * It must exist in query components list.
517     *
518     * @return void
519     */
520    private function _processDeferredIdentificationVariables()
521    {
522        foreach ($this->_deferredIdentificationVariables as $deferredItem) {
523            $identVariable = $deferredItem['expression'];
524
525            // Check if IdentificationVariable exists in queryComponents
526            if ( ! isset($this->_queryComponents[$identVariable])) {
527                $this->semanticalError(
528                    "'$identVariable' is not defined.", $deferredItem['token']
529                );
530            }
531
532            $qComp = $this->_queryComponents[$identVariable];
533
534            // Check if queryComponent points to an AbstractSchemaName or a ResultVariable
535            if ( ! isset($qComp['metadata'])) {
536                $this->semanticalError(
537                    "'$identVariable' does not point to a Class.", $deferredItem['token']
538                );
539            }
540
541            // Validate if identification variable nesting level is lower or equal than the current one
542            if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) {
543                $this->semanticalError(
544                    "'$identVariable' is used outside the scope of its declaration.", $deferredItem['token']
545                );
546            }
547        }
548    }
549
550    /**
551     * Validates that the given <tt>PartialObjectExpression</tt> is semantically correct.
552     * It must exist in query components list.
553     *
554     * @return void
555     */
556    private function _processDeferredPartialObjectExpressions()
557    {
558        foreach ($this->_deferredPartialObjectExpressions as $deferredItem) {
559            $expr = $deferredItem['expression'];
560            $class = $this->_queryComponents[$expr->identificationVariable]['metadata'];
561
562            foreach ($expr->partialFieldSet as $field) {
563                if (isset($class->fieldMappings[$field])) {
564                        continue;
565                }
566
567                $this->semanticalError(
568                    "There is no mapped field named '$field' on class " . $class->name . ".", $deferredItem['token']
569                );
570            }
571
572            if (array_intersect($class->identifier, $expr->partialFieldSet) != $class->identifier) {
573                $this->semanticalError(
574                    "The partial field selection of class " . $class->name . " must contain the identifier.",
575                    $deferredItem['token']
576                );
577            }
578        }
579    }
580
581    /**
582     * Validates that the given <tt>ResultVariable</tt> is semantically correct.
583     * It must exist in query components list.
584     *
585     * @return void
586     */
587    private function _processDeferredResultVariables()
588    {
589        foreach ($this->_deferredResultVariables as $deferredItem) {
590            $resultVariable = $deferredItem['expression'];
591
592            // Check if ResultVariable exists in queryComponents
593            if ( ! isset($this->_queryComponents[$resultVariable])) {
594                $this->semanticalError(
595                    "'$resultVariable' is not defined.", $deferredItem['token']
596                );
597            }
598
599            $qComp = $this->_queryComponents[$resultVariable];
600
601            // Check if queryComponent points to an AbstractSchemaName or a ResultVariable
602            if ( ! isset($qComp['resultVariable'])) {
603                $this->semanticalError(
604                    "'$identVariable' does not point to a ResultVariable.", $deferredItem['token']
605                );
606            }
607
608            // Validate if identification variable nesting level is lower or equal than the current one
609            if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) {
610                $this->semanticalError(
611                    "'$resultVariable' is used outside the scope of its declaration.", $deferredItem['token']
612                );
613            }
614        }
615    }
616
617    /**
618     * Validates that the given <tt>PathExpression</tt> is semantically correct for grammar rules:
619     *
620     * AssociationPathExpression             ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression
621     * SingleValuedPathExpression            ::= StateFieldPathExpression | SingleValuedAssociationPathExpression
622     * StateFieldPathExpression              ::= IdentificationVariable "." StateField
623     * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField
624     * CollectionValuedPathExpression        ::= IdentificationVariable "." CollectionValuedAssociationField
625     *
626     * @param array $deferredItem
627     * @param mixed $AST
628     */
629    private function _processDeferredPathExpressions($AST)
630    {
631        foreach ($this->_deferredPathExpressions as $deferredItem) {
632            $pathExpression = $deferredItem['expression'];
633
634            $qComp = $this->_queryComponents[$pathExpression->identificationVariable];
635            $class = $qComp['metadata'];
636
637            if (($field = $pathExpression->field) === null) {
638                $field = $pathExpression->field = $class->identifier[0];
639            }
640
641            // Check if field or association exists
642            if ( ! isset($class->associationMappings[$field]) && ! isset($class->fieldMappings[$field])) {
643                $this->semanticalError(
644                    'Class ' . $class->name . ' has no field or association named ' . $field,
645                    $deferredItem['token']
646                );
647            }
648
649            $fieldType = AST\PathExpression::TYPE_STATE_FIELD;
650
651            if (isset($class->associationMappings[$field])) {
652                $assoc = $class->associationMappings[$field];
653
654                $fieldType = ($assoc['type'] & ClassMetadata::TO_ONE)
655                        ? AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION
656                        : AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION;
657            }
658
659            // Validate if PathExpression is one of the expected types
660            $expectedType = $pathExpression->expectedType;
661
662            if ( ! ($expectedType & $fieldType)) {
663                // We need to recognize which was expected type(s)
664                $expectedStringTypes = array();
665
666                // Validate state field type
667                if ($expectedType & AST\PathExpression::TYPE_STATE_FIELD) {
668                    $expectedStringTypes[] = 'StateFieldPathExpression';
669                }
670
671                // Validate single valued association (*-to-one)
672                if ($expectedType & AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION) {
673                    $expectedStringTypes[] = 'SingleValuedAssociationField';
674                }
675
676                // Validate single valued association (*-to-many)
677                if ($expectedType & AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION) {
678                    $expectedStringTypes[] = 'CollectionValuedAssociationField';
679                }
680
681                // Build the error message
682                $semanticalError  = 'Invalid PathExpression. ';
683                $semanticalError .= (count($expectedStringTypes) == 1)
684                    ? 'Must be a ' . $expectedStringTypes[0] . '.'
685                    : implode(' or ', $expectedStringTypes) . ' expected.';
686
687                $this->semanticalError($semanticalError, $deferredItem['token']);
688            }
689
690            // We need to force the type in PathExpression
691            $pathExpression->type = $fieldType;
692        }
693    }
694
695    private function _processRootEntityAliasSelected()
696    {
697        if ( ! count($this->_identVariableExpressions)) {
698            return;
699        }
700
701        $foundRootEntity = false;
702
703        foreach ($this->_identVariableExpressions AS $dqlAlias => $expr) {
704            if (isset($this->_queryComponents[$dqlAlias]) && $this->_queryComponents[$dqlAlias]['parent'] === null) {
705                $foundRootEntity = true;
706            }
707        }
708
709        if ( ! $foundRootEntity) {
710            $this->semanticalError('Cannot select entity through identification variables without choosing at least one root entity alias.');
711        }
712    }
713
714    /**
715     * QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement
716     *
717     * @return \Doctrine\ORM\Query\AST\SelectStatement |
718     *         \Doctrine\ORM\Query\AST\UpdateStatement |
719     *         \Doctrine\ORM\Query\AST\DeleteStatement
720     */
721    public function QueryLanguage()
722    {
723        $this->_lexer->moveNext();
724
725        switch ($this->_lexer->lookahead['type']) {
726            case Lexer::T_SELECT:
727                $statement = $this->SelectStatement();
728                break;
729
730            case Lexer::T_UPDATE:
731                $statement = $this->UpdateStatement();
732                break;
733
734            case Lexer::T_DELETE:
735                $statement = $this->DeleteStatement();
736                break;
737
738            default:
739                $this->syntaxError('SELECT, UPDATE or DELETE');
740                break;
741        }
742
743        // Check for end of string
744        if ($this->_lexer->lookahead !== null) {
745            $this->syntaxError('end of string');
746        }
747
748        return $statement;
749    }
750
751    /**
752     * SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
753     *
754     * @return \Doctrine\ORM\Query\AST\SelectStatement
755     */
756    public function SelectStatement()
757    {
758        $selectStatement = new AST\SelectStatement($this->SelectClause(), $this->FromClause());
759
760        $selectStatement->whereClause   = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
761        $selectStatement->groupByClause = $this->_lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null;
762        $selectStatement->havingClause  = $this->_lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null;
763        $selectStatement->orderByClause = $this->_lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null;
764
765        return $selectStatement;
766    }
767
768    /**
769     * UpdateStatement ::= UpdateClause [WhereClause]
770     *
771     * @return \Doctrine\ORM\Query\AST\UpdateStatement
772     */
773    public function UpdateStatement()
774    {
775        $updateStatement = new AST\UpdateStatement($this->UpdateClause());
776
777        $updateStatement->whereClause = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
778
779        return $updateStatement;
780    }
781
782    /**
783     * DeleteStatement ::= DeleteClause [WhereClause]
784     *
785     * @return \Doctrine\ORM\Query\AST\DeleteStatement
786     */
787    public function DeleteStatement()
788    {
789        $deleteStatement = new AST\DeleteStatement($this->DeleteClause());
790
791        $deleteStatement->whereClause = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
792
793        return $deleteStatement;
794    }
795
796    /**
797     * IdentificationVariable ::= identifier
798     *
799     * @return string
800     */
801    public function IdentificationVariable()
802    {
803        $this->match(Lexer::T_IDENTIFIER);
804
805        $identVariable = $this->_lexer->token['value'];
806
807        $this->_deferredIdentificationVariables[] = array(
808            'expression'   => $identVariable,
809            'nestingLevel' => $this->_nestingLevel,
810            'token'        => $this->_lexer->token,
811        );
812
813        return $identVariable;
814    }
815
816    /**
817     * AliasIdentificationVariable = identifier
818     *
819     * @return string
820     */
821    public function AliasIdentificationVariable()
822    {
823        $this->match(Lexer::T_IDENTIFIER);
824
825        $aliasIdentVariable = $this->_lexer->token['value'];
826        $exists = isset($this->_queryComponents[$aliasIdentVariable]);
827
828        if ($exists) {
829            $this->semanticalError("'$aliasIdentVariable' is already defined.", $this->_lexer->token);
830        }
831
832        return $aliasIdentVariable;
833    }
834
835    /**
836     * AbstractSchemaName ::= identifier
837     *
838     * @return string
839     */
840    public function AbstractSchemaName()
841    {
842        $this->match(Lexer::T_IDENTIFIER);
843
844        $schemaName = ltrim($this->_lexer->token['value'], '\\');
845
846        if (strrpos($schemaName, ':') !== false) {
847            list($namespaceAlias, $simpleClassName) = explode(':', $schemaName);
848
849            $schemaName = $this->_em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
850        }
851
852        $exists = class_exists($schemaName, true);
853
854        if ( ! $exists) {
855            $this->semanticalError("Class '$schemaName' is not defined.", $this->_lexer->token);
856        }
857
858        return $schemaName;
859    }
860
861    /**
862     * AliasResultVariable ::= identifier
863     *
864     * @return string
865     */
866    public function AliasResultVariable()
867    {
868        $this->match(Lexer::T_IDENTIFIER);
869
870        $resultVariable = $this->_lexer->token['value'];
871        $exists = isset($this->_queryComponents[$resultVariable]);
872
873        if ($exists) {
874            $this->semanticalError("'$resultVariable' is already defined.", $this->_lexer->token);
875        }
876
877        return $resultVariable;
878    }
879
880    /**
881     * ResultVariable ::= identifier
882     *
883     * @return string
884     */
885    public function ResultVariable()
886    {
887        $this->match(Lexer::T_IDENTIFIER);
888
889        $resultVariable = $this->_lexer->token['value'];
890
891        // Defer ResultVariable validation
892        $this->_deferredResultVariables[] = array(
893            'expression'   => $resultVariable,
894            'nestingLevel' => $this->_nestingLevel,
895            'token'        => $this->_lexer->token,
896        );
897
898        return $resultVariable;
899    }
900
901    /**
902     * JoinAssociationPathExpression ::= IdentificationVariable "." (CollectionValuedAssociationField | SingleValuedAssociationField)
903     *
904     * @return \Doctrine\ORM\Query\AST\JoinAssociationPathExpression
905     */
906    public function JoinAssociationPathExpression()
907    {
908        $token         = $this->_lexer->lookahead;
909        $identVariable = $this->IdentificationVariable();
910
911        if ( ! isset($this->_queryComponents[$identVariable])) {
912            $this->semanticalError(
913                'Identification Variable ' . $identVariable .' used in join path expression but was not defined before.'
914            );
915        }
916
917        $this->match(Lexer::T_DOT);
918        $this->match(Lexer::T_IDENTIFIER);
919
920        $field = $this->_lexer->token['value'];
921
922        // Validate association field
923        $qComp = $this->_queryComponents[$identVariable];
924        $class = $qComp['metadata'];
925
926        if ( ! isset($class->associationMappings[$field])) {
927            $this->semanticalError('Class ' . $class->name . ' has no association named ' . $field);
928        }
929
930        return new AST\JoinAssociationPathExpression($identVariable, $field);
931    }
932
933    /**
934     * Parses an arbitrary path expression and defers semantical validation
935     * based on expected types.
936     *
937     * PathExpression ::= IdentificationVariable "." identifier
938     *
939     * @param integer $expectedTypes
940     * @return \Doctrine\ORM\Query\AST\PathExpression
941     */
942    public function PathExpression($expectedTypes)
943    {
944        $token = $this->_lexer->lookahead;
945        $identVariable = $this->IdentificationVariable();
946        $field = null;
947
948        if ($this->_lexer->isNextToken(Lexer::T_DOT)) {
949            $this->match(Lexer::T_DOT);
950            $this->match(Lexer::T_IDENTIFIER);
951
952            $field = $this->_lexer->token['value'];
953        }
954
955        // Creating AST node
956        $pathExpr = new AST\PathExpression($expectedTypes, $identVariable, $field);
957
958        // Defer PathExpression validation if requested to be defered
959        $this->_deferredPathExpressions[] = array(
960            'expression'   => $pathExpr,
961            'nestingLevel' => $this->_nestingLevel,
962            'token'        => $this->_lexer->token,
963        );
964
965        return $pathExpr;
966    }
967
968    /**
969     * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression
970     *
971     * @return \Doctrine\ORM\Query\AST\PathExpression
972     */
973    public function AssociationPathExpression()
974    {
975        return $this->PathExpression(
976            AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION |
977            AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION
978        );
979    }
980
981    /**
982     * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression
983     *
984     * @return \Doctrine\ORM\Query\AST\PathExpression
985     */
986    public function SingleValuedPathExpression()
987    {
988        return $this->PathExpression(
989            AST\PathExpression::TYPE_STATE_FIELD |
990            AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION
991        );
992    }
993
994    /**
995     * StateFieldPathExpression ::= IdentificationVariable "." StateField
996     *
997     * @return \Doctrine\ORM\Query\AST\PathExpression
998     */
999    public function StateFieldPathExpression()
1000    {
1001        return $this->PathExpression(AST\PathExpression::TYPE_STATE_FIELD);
1002    }
1003
1004    /**
1005     * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField
1006     *
1007     * @return \Doctrine\ORM\Query\AST\PathExpression
1008     */
1009    public function SingleValuedAssociationPathExpression()
1010    {
1011        return $this->PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION);
1012    }
1013
1014    /**
1015     * CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField
1016     *
1017     * @return \Doctrine\ORM\Query\AST\PathExpression
1018     */
1019    public function CollectionValuedPathExpression()
1020    {
1021        return $this->PathExpression(AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION);
1022    }
1023
1024    /**
1025     * SelectClause ::= "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression}
1026     *
1027     * @return \Doctrine\ORM\Query\AST\SelectClause
1028     */
1029    public function SelectClause()
1030    {
1031        $isDistinct = false;
1032        $this->match(Lexer::T_SELECT);
1033
1034        // Check for DISTINCT
1035        if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) {
1036            $this->match(Lexer::T_DISTINCT);
1037
1038            $isDistinct = true;
1039        }
1040
1041        // Process SelectExpressions (1..N)
1042        $selectExpressions = array();
1043        $selectExpressions[] = $this->SelectExpression();
1044
1045        while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
1046            $this->match(Lexer::T_COMMA);
1047
1048            $selectExpressions[] = $this->SelectExpression();
1049        }
1050
1051        return new AST\SelectClause($selectExpressions, $isDistinct);
1052    }
1053
1054    /**
1055     * SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression
1056     *
1057     * @return \Doctrine\ORM\Query\AST\SimpleSelectClause
1058     */
1059    public function SimpleSelectClause()
1060    {
1061        $isDistinct = false;
1062        $this->match(Lexer::T_SELECT);
1063
1064        if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) {
1065            $this->match(Lexer::T_DISTINCT);
1066
1067            $isDistinct = true;
1068        }
1069
1070        return new AST\SimpleSelectClause($this->SimpleSelectExpression(), $isDistinct);
1071    }
1072
1073    /**
1074     * UpdateClause ::= "UPDATE" AbstractSchemaName ["AS"] AliasIdentificationVariable "SET" UpdateItem {"," UpdateItem}*
1075     *
1076     * @return \Doctrine\ORM\Query\AST\UpdateClause
1077     */
1078    public function UpdateClause()
1079    {
1080        $this->match(Lexer::T_UPDATE);
1081        $token = $this->_lexer->lookahead;
1082        $abstractSchemaName = $this->AbstractSchemaName();
1083
1084        if ($this->_lexer->isNextToken(Lexer::T_AS)) {
1085            $this->match(Lexer::T_AS);
1086        }
1087
1088        $aliasIdentificationVariable = $this->AliasIdentificationVariable();
1089
1090        $class = $this->_em->getClassMetadata($abstractSchemaName);
1091
1092        // Building queryComponent
1093        $queryComponent = array(
1094            'metadata'     => $class,
1095            'parent'       => null,
1096            'relation'     => null,
1097            'map'          => null,
1098            'nestingLevel' => $this->_nestingLevel,
1099            'token'        => $token,
1100        );
1101
1102        $this->_queryComponents[$aliasIdentificationVariable] = $queryComponent;
1103
1104        $this->match(Lexer::T_SET);
1105
1106        $updateItems = array();
1107        $updateItems[] = $this->UpdateItem();
1108
1109        while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
1110            $this->match(Lexer::T_COMMA);
1111
1112            $updateItems[] = $this->UpdateItem();
1113        }
1114
1115        $updateClause = new AST\UpdateClause($abstractSchemaName, $updateItems);
1116        $updateClause->aliasIdentificationVariable = $aliasIdentificationVariable;
1117
1118        return $updateClause;
1119    }
1120
1121    /**
1122     * DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName ["AS"] AliasIdentificationVariable
1123     *
1124     * @return \Doctrine\ORM\Query\AST\DeleteClause
1125     */
1126    public function DeleteClause()
1127    {
1128        $this->match(Lexer::T_DELETE);
1129
1130        if ($this->_lexer->isNextToken(Lexer::T_FROM)) {
1131            $this->match(Lexer::T_FROM);
1132        }
1133
1134        $token = $this->_lexer->lookahead;
1135        $deleteClause = new AST\DeleteClause($this->AbstractSchemaName());
1136
1137        if ($this->_lexer->isNextToken(Lexer::T_AS)) {
1138            $this->match(Lexer::T_AS);
1139        }
1140
1141        $aliasIdentificationVariable = $this->AliasIdentificationVariable();
1142
1143        $deleteClause->aliasIdentificationVariable = $aliasIdentificationVariable;
1144        $class = $this->_em->getClassMetadata($deleteClause->abstractSchemaName);
1145
1146        // Building queryComponent
1147        $queryComponent = array(
1148            'metadata'     => $class,
1149            'parent'       => null,
1150            'relation'     => null,
1151            'map'          => null,
1152            'nestingLevel' => $this->_nestingLevel,
1153            'token'        => $token,
1154        );
1155
1156        $this->_queryComponents[$aliasIdentificationVariable] = $queryComponent;
1157
1158        return $deleteClause;
1159    }
1160
1161    /**
1162     * FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}*
1163     *
1164     * @return \Doctrine\ORM\Query\AST\FromClause
1165     */
1166    public function FromClause()
1167    {
1168        $this->match(Lexer::T_FROM);
1169
1170        $identificationVariableDeclarations = array();
1171        $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration();
1172
1173        while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
1174            $this->match(Lexer::T_COMMA);
1175
1176            $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration();
1177        }
1178
1179        return new AST\FromClause($identificationVariableDeclarations);
1180    }
1181
1182    /**
1183     * SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}*
1184     *
1185     * @return \Doctrine\ORM\Query\AST\SubselectFromClause
1186     */
1187    public function SubselectFromClause()
1188    {
1189        $this->match(Lexer::T_FROM);
1190
1191        $identificationVariables = array();
1192        $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration();
1193
1194        while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
1195            $this->match(Lexer::T_COMMA);
1196
1197            $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration();
1198        }
1199
1200        return new AST\SubselectFromClause($identificationVariables);
1201    }
1202
1203    /**
1204     * WhereClause ::= "WHERE" ConditionalExpression
1205     *
1206     * @return \Doctrine\ORM\Query\AST\WhereClause
1207     */
1208    public function WhereClause()
1209    {
1210        $this->match(Lexer::T_WHERE);
1211
1212        return new AST\WhereClause($this->ConditionalExpression());
1213    }
1214
1215    /**
1216     * HavingClause ::= "HAVING" ConditionalExpression
1217     *
1218     * @return \Doctrine\ORM\Query\AST\HavingClause
1219     */
1220    public function HavingClause()
1221    {
1222        $this->match(Lexer::T_HAVING);
1223
1224        return new AST\HavingClause($this->ConditionalExpression());
1225    }
1226
1227    /**
1228     * GroupByClause ::= "GROUP" "BY" GroupByItem {"," GroupByItem}*
1229     *
1230     * @return \Doctrine\ORM\Query\AST\GroupByClause
1231     */
1232    public function GroupByClause()
1233    {
1234        $this->match(Lexer::T_GROUP);
1235        $this->match(Lexer::T_BY);
1236
1237        $groupByItems = array($this->GroupByItem());
1238
1239        while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
1240            $this->match(Lexer::T_COMMA);
1241
1242            $groupByItems[] = $this->GroupByItem();
1243        }
1244
1245        return new AST\GroupByClause($groupByItems);
1246    }
1247
1248    /**
1249     * OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}*
1250     *
1251     * @return \Doctrine\ORM\Query\AST\OrderByClause
1252     */
1253    public function OrderByClause()
1254    {
1255        $this->match(Lexer::T_ORDER);
1256        $this->match(Lexer::T_BY);
1257
1258        $orderByItems = array();
1259        $orderByItems[] = $this->OrderByItem();
1260
1261        while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
1262            $this->match(Lexer::T_COMMA);
1263
1264            $orderByItems[] = $this->OrderByItem();
1265        }
1266
1267        return new AST\OrderByClause($orderByItems);
1268    }
1269
1270    /**
1271     * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
1272     *
1273     * @return \Doctrine\ORM\Query\AST\Subselect
1274     */
1275    public function Subselect()
1276    {
1277        // Increase query nesting level
1278        $this->_nestingLevel++;
1279
1280        $subselect = new AST\Subselect($this->SimpleSelectClause(), $this->SubselectFromClause());
1281
1282        $subselect->whereClause   = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
1283        $subselect->groupByClause = $this->_lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null;
1284        $subselect->havingClause  = $this->_lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null;
1285        $subselect->orderByClause = $this->_lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null;
1286
1287        // Decrease query nesting level
1288        $this->_nestingLevel--;
1289
1290        return $subselect;
1291    }
1292
1293    /**
1294     * UpdateItem ::= SingleValuedPathExpression "=" NewValue
1295     *
1296     * @return \Doctrine\ORM\Query\AST\UpdateItem
1297     */
1298    public function UpdateItem()
1299    {
1300        $pathExpr = $this->SingleValuedPathExpression();
1301
1302        $this->match(Lexer::T_EQUALS);
1303
1304        $updateItem = new AST\UpdateItem($pathExpr, $this->NewValue());
1305
1306        return $updateItem;
1307    }
1308
1309    /**
1310     * GroupByItem ::= IdentificationVariable | ResultVariable | SingleValuedPathExpression
1311     *
1312     * @return string | \Doctrine\ORM\Query\AST\PathExpression
1313     */
1314    public function GroupByItem()
1315    {
1316        // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression
1317        $glimpse = $this->_lexer->glimpse();
1318
1319        if ($glimpse['type'] === Lexer::T_DOT) {
1320            return $this->SingleValuedPathExpression();
1321        }
1322
1323        // Still need to decide between IdentificationVariable or ResultVariable
1324        $lookaheadValue = $this->_lexer->lookahead['value'];
1325
1326        if ( ! isset($this->_queryComponents[$lookaheadValue])) {
1327            $this->semanticalError('Cannot group by undefined identification or result variable.');
1328        }
1329
1330        return (isset($this->_queryComponents[$lookaheadValue]['metadata']))
1331            ? $this->IdentificationVariable()
1332            : $this->ResultVariable();
1333    }
1334
1335    /**
1336     * OrderByItem ::= (ResultVariable | SingleValuedPathExpression) ["ASC" | "DESC"]
1337     *
1338     * @return \Doctrine\ORM\Query\AST\OrderByItem
1339     */
1340    public function OrderByItem()
1341    {
1342        $type = 'ASC';
1343
1344        // We need to check if we are in a ResultVariable or StateFieldPathExpression
1345        $glimpse = $this->_lexer->glimpse();
1346        $expr    = ($glimpse['type'] != Lexer::T_DOT) ? $this->ResultVariable() : $this->SingleValuedPathExpression();
1347
1348        $item = new AST\OrderByItem($expr);
1349
1350        switch (true) {
1351            case ($this->_lexer->isNextToken(Lexer::T_DESC)):
1352                $this->match(Lexer::T_DESC);
1353                $type = 'DESC';
1354                break;
1355
1356            case ($this->_lexer->isNextToken(Lexer::T_ASC)):
1357                $this->match(Lexer::T_ASC);
1358                break;
1359
1360            default:
1361                // Do nothing
1362        }
1363
1364        $item->type = $type;
1365
1366        return $item;
1367    }
1368
1369    /**
1370     * NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary |
1371     *      EnumPrimary | SimpleEntityExpression | "NULL"
1372     *
1373     * NOTE: Since it is not possible to correctly recognize individual types, here is the full
1374     * grammar that needs to be supported:
1375     *
1376     * NewValue ::= SimpleArithmeticExpression | "NULL"
1377     *
1378     * SimpleArithmeticExpression covers all *Primary grammar rules and also SimplEntityExpression
1379     */
1380    public function NewValue()
1381    {
1382        if ($this->_lexer->isNextToken(Lexer::T_NULL)) {
1383            $this->match(Lexer::T_NULL);
1384
1385            return null;
1386        }
1387
1388        if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
1389            $this->match(Lexer::T_INPUT_PARAMETER);
1390
1391            return new AST\InputParameter($this->_lexer->token['value']);
1392        }
1393
1394        return $this->SimpleArithmeticExpression();
1395    }
1396
1397    /**
1398     * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {JoinVariableDeclaration}*
1399     *
1400     * @return \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration
1401     */
1402    public function IdentificationVariableDeclaration()
1403    {
1404        $rangeVariableDeclaration = $this->RangeVariableDeclaration();
1405        $indexBy = $this->_lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null;
1406        $joinVariableDeclarations = array();
1407
1408        while (
1409            $this->_lexer->isNextToken(Lexer::T_LEFT) ||
1410            $this->_lexer->isNextToken(Lexer::T_INNER) ||
1411            $this->_lexer->isNextToken(Lexer::T_JOIN)
1412        ) {
1413            $joinVariableDeclarations[] = $this->JoinVariableDeclaration();
1414        }
1415
1416        return new AST\IdentificationVariableDeclaration(
1417            $rangeVariableDeclaration, $indexBy, $joinVariableDeclarations
1418        );
1419    }
1420
1421    /**
1422     * SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration | (AssociationPathExpression ["AS"] AliasIdentificationVariable)
1423     *
1424     * @return \Doctrine\ORM\Query\AST\SubselectIdentificationVariableDeclaration |
1425     *         \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration
1426     */
1427    public function SubselectIdentificationVariableDeclaration()
1428    {
1429        $glimpse = $this->_lexer->glimpse();
1430
1431        /* NOT YET IMPLEMENTED!
1432
1433        if ($glimpse['type'] == Lexer::T_DOT) {
1434            $subselectIdVarDecl = new AST\SubselectIdentificationVariableDeclaration();
1435            $subselectIdVarDecl->associationPathExpression = $this->AssociationPathExpression();
1436            $this->match(Lexer::T_AS);
1437            $subselectIdVarDecl->aliasIdentificationVariable = $this->AliasIdentificationVariable();
1438
1439            return $subselectIdVarDecl;
1440        }
1441        */
1442
1443        return $this->IdentificationVariableDeclaration();
1444    }
1445
1446    /**
1447     * JoinVariableDeclaration ::= Join [IndexBy]
1448     *
1449     * @return \Doctrine\ORM\Query\AST\JoinVariableDeclaration
1450     */
1451    public function JoinVariableDeclaration()
1452    {
1453        $join    = $this->Join();
1454        $indexBy = $this->_lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null;
1455
1456        return new AST\JoinVariableDeclaration($join, $indexBy);
1457    }
1458
1459    /**
1460     * RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable
1461     *
1462     * @return \Doctrine\ORM\Query\AST\RangeVariableDeclaration
1463     */
1464    public function RangeVariableDeclaration()
1465    {
1466        $abstractSchemaName = $this->AbstractSchemaName();
1467
1468        if ($this->_lexer->isNextToken(Lexer::T_AS)) {
1469            $this->match(Lexer::T_AS);
1470        }
1471
1472        $token = $this->_lexer->lookahead;
1473        $aliasIdentificationVariable = $this->AliasIdentificationVariable();
1474        $classMetadata = $this->_em->getClassMetadata($abstractSchemaName);
1475
1476        // Building queryComponent
1477        $queryComponent = array(
1478            'metadata'     => $classMetadata,
1479            'parent'       => null,
1480            'relation'     => null,
1481            'map'          => null,
1482            'nestingLevel' => $this->_nestingLevel,
1483            'token'        => $token
1484        );
1485
1486        $this->_queryComponents[$aliasIdentificationVariable] = $queryComponent;
1487
1488        return new AST\RangeVariableDeclaration($abstractSchemaName, $aliasIdentificationVariable);
1489    }
1490
1491    /**
1492     * PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet
1493     * PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}"
1494     *
1495     * @return array
1496     */
1497    public function PartialObjectExpression()
1498    {
1499        $this->match(Lexer::T_PARTIAL);
1500
1501        $partialFieldSet = array();
1502
1503        $identificationVariable = $this->IdentificationVariable();
1504
1505        $this->match(Lexer::T_DOT);
1506        $this->match(Lexer::T_OPEN_CURLY_BRACE);
1507        $this->match(Lexer::T_IDENTIFIER);
1508
1509        $partialFieldSet[] = $this->_lexer->token['value'];
1510
1511        while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
1512            $this->match(Lexer::T_COMMA);
1513            $this->match(Lexer::T_IDENTIFIER);
1514
1515            $partialFieldSet[] = $this->_lexer->token['value'];
1516        }
1517
1518        $this->match(Lexer::T_CLOSE_CURLY_BRACE);
1519
1520        $partialObjectExpression = new AST\PartialObjectExpression($identificationVariable, $partialFieldSet);
1521
1522        // Defer PartialObjectExpression validation
1523        $this->_deferredPartialObjectExpressions[] = array(
1524            'expression'   => $partialObjectExpression,
1525            'nestingLevel' => $this->_nestingLevel,
1526            'token'        => $this->_lexer->token,
1527        );
1528
1529        return $partialObjectExpression;
1530    }
1531
1532    /**
1533     * Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" JoinAssociationPathExpression
1534     *          ["AS"] AliasIdentificationVariable ["WITH" ConditionalExpression]
1535     *
1536     * @return \Doctrine\ORM\Query\AST\Join
1537     */
1538    public function Join()
1539    {
1540        // Check Join type
1541        $joinType = AST\Join::JOIN_TYPE_INNER;
1542
1543        switch (true) {
1544            case ($this->_lexer->isNextToken(Lexer::T_LEFT)):
1545                $this->match(Lexer::T_LEFT);
1546
1547                $joinType = AST\Join::JOIN_TYPE_LEFT;
1548
1549                // Possible LEFT OUTER join
1550                if ($this->_lexer->isNextToken(Lexer::T_OUTER)) {
1551                    $this->match(Lexer::T_OUTER);
1552
1553                    $joinType = AST\Join::JOIN_TYPE_LEFTOUTER;
1554                }
1555                break;
1556
1557            case ($this->_lexer->isNextToken(Lexer::T_INNER)):
1558                $this->match(Lexer::T_INNER);
1559                break;
1560
1561            default:
1562                // Do nothing
1563        }
1564
1565        $this->match(Lexer::T_JOIN);
1566
1567        $joinPathExpression = $this->JoinAssociationPathExpression();
1568
1569        if ($this->_lexer->isNextToken(Lexer::T_AS)) {
1570            $this->match(Lexer::T_AS);
1571        }
1572
1573        $token = $this->_lexer->lookahead;
1574        $aliasIdentificationVariable = $this->AliasIdentificationVariable();
1575
1576        // Verify that the association exists.
1577        $parentClass = $this->_queryComponents[$joinPathExpression->identificationVariable]['metadata'];
1578        $assocField  = $joinPathExpression->associationField;
1579
1580        if ( ! $parentClass->hasAssociation($assocField)) {
1581            $this->semanticalError(
1582                "Class " . $parentClass->name . " has no association named '$assocField'."
1583            );
1584        }
1585
1586        $targetClassName = $parentClass->associationMappings[$assocField]['targetEntity'];
1587
1588        // Building queryComponent
1589        $joinQueryComponent = array(
1590            'metadata'     => $this->_em->getClassMetadata($targetClassName),
1591            'parent'       => $joinPathExpression->identificationVariable,
1592            'relation'     => $parentClass->getAssociationMapping($assocField),
1593            'map'          => null,
1594            'nestingLevel' => $this->_nestingLevel,
1595            'token'        => $token
1596        );
1597
1598        $this->_queryComponents[$aliasIdentificationVariable] = $joinQueryComponent;
1599
1600        // Create AST node
1601        $join = new AST\Join($joinType, $joinPathExpression, $aliasIdentificationVariable);
1602
1603        // Check for ad-hoc Join conditions
1604        if ($this->_lexer->isNextToken(Lexer::T_WITH)) {
1605            $this->match(Lexer::T_WITH);
1606
1607            $join->conditionalExpression = $this->ConditionalExpression();
1608        }
1609
1610        return $join;
1611    }
1612
1613    /**
1614     * IndexBy ::= "INDEX" "BY" StateFieldPathExpression
1615     *
1616     * @return \Doctrine\ORM\Query\AST\IndexBy
1617     */
1618    public function IndexBy()
1619    {
1620        $this->match(Lexer::T_INDEX);
1621        $this->match(Lexer::T_BY);
1622        $pathExpr = $this->StateFieldPathExpression();
1623
1624        // Add the INDEX BY info to the query component
1625        $this->_queryComponents[$pathExpr->identificationVariable]['map'] = $pathExpr->field;
1626
1627        return new AST\IndexBy($pathExpr);
1628    }
1629
1630    /**
1631     * ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary |
1632     *                      StateFieldPathExpression | BooleanPrimary | CaseExpression |
1633     *                      InstanceOfExpression
1634     *
1635     * @return mixed One of the possible expressions or subexpressions.
1636     */
1637    public function ScalarExpression()
1638    {
1639        $lookahead = $this->_lexer->lookahead['type'];
1640
1641        switch ($lookahead) {
1642            case Lexer::T_IDENTIFIER:
1643                $this->_lexer->peek(); // lookahead => '.'
1644                $this->_lexer->peek(); // lookahead => token after '.'
1645                $peek = $this->_lexer->peek(); // lookahead => token after the token after the '.'
1646                $this->_lexer->resetPeek();
1647
1648                if ($this->_isMathOperator($peek)) {
1649                    return $this->SimpleArithmeticExpression();
1650                }
1651
1652                return $this->StateFieldPathExpression();
1653
1654            case Lexer::T_INTEGER:
1655            case Lexer::T_FLOAT:
1656                return $this->SimpleArithmeticExpression();
1657
1658            case Lexer::T_STRING:
1659                return $this->StringPrimary();
1660
1661            case Lexer::T_TRUE:
1662            case Lexer::T_FALSE:
1663                $this->match($lookahead);
1664
1665                return new AST\Literal(AST\Literal::BOOLEAN, $this->_lexer->token['value']);
1666
1667            case Lexer::T_INPUT_PARAMETER:
1668                return $this->inputsParameter();
1669
1670            case Lexer::T_CASE:
1671            case Lexer::T_COALESCE:
1672            case Lexer::T_NULLIF:
1673                // Since NULLIF and COALESCE can be identified as a function,
1674                // we need to check if before check for FunctionDeclaration
1675                return $this->CaseExpression();
1676
1677            default:
1678                if ( ! ($this->_isFunction() || $this->_isAggregateFunction($lookahead))) {
1679                    $this->syntaxError();
1680                }
1681
1682                // We may be in an ArithmeticExpression (find the matching ")" and inspect for Math operator)
1683                $this->_lexer->peek(); // "("
1684                $peek = $this->_peekBeyondClosingParenthesis();
1685
1686                if ($this->_isMathOperator($peek)) {
1687                    return $this->SimpleArithmeticExpression();
1688                }
1689
1690                if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
1691                    return $this->AggregateExpression();
1692                }
1693
1694                return $this->FunctionDeclaration();
1695        }
1696    }
1697
1698    /**
1699     * CaseExpression ::= GeneralCaseExpression | SimpleCaseExpression | CoalesceExpression | NullifExpression
1700     * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END"
1701     * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression
1702     * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END"
1703     * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator
1704     * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression
1705     * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
1706     * NullifExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
1707     *
1708     * @return mixed One of the possible expressions or subexpressions.
1709     */
1710    public function CaseExpression()
1711    {
1712        $lookahead = $this->_lexer->lookahead['type'];
1713
1714        switch ($lookahead) {
1715            case Lexer::T_NULLIF:
1716                return $this->NullIfExpression();
1717
1718            case Lexer::T_COALESCE:
1719                return $this->CoalesceExpression();
1720
1721            case Lexer::T_CASE:
1722                $this->_lexer->resetPeek();
1723                $peek = $this->_lexer->peek();
1724
1725                if ($peek['type'] === Lexer::T_WHEN) {
1726                    return $this->GeneralCaseExpression();
1727                }
1728
1729                return $this->SimpleCaseExpression();
1730
1731            default:
1732                // Do nothing
1733                break;
1734        }
1735
1736        $this->syntaxError();
1737    }
1738
1739    /**
1740     * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
1741     *
1742     * @return \Doctrine\ORM\Query\AST\CoalesceExpression
1743     */
1744    public function CoalesceExpression()
1745    {
1746        $this->match(Lexer::T_COALESCE);
1747        $this->match(Lexer::T_OPEN_PARENTHESIS);
1748
1749        // Process ScalarExpressions (1..N)
1750        $scalarExpressions = array();
1751        $scalarExpressions[] = $this->ScalarExpression();
1752
1753        while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
1754            $this->match(Lexer::T_COMMA);
1755
1756            $scalarExpressions[] = $this->ScalarExpression();
1757        }
1758
1759        $this->match(Lexer::T_CLOSE_PARENTHESIS);
1760
1761        return new AST\CoalesceExpression($scalarExpressions);
1762    }
1763
1764    /**
1765     * NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
1766     *
1767     * @return \Doctrine\ORM\Query\AST\NullIfExpression
1768     */
1769    public function NullIfExpression()
1770    {
1771        $this->match(Lexer::T_NULLIF);
1772        $this->match(Lexer::T_OPEN_PARENTHESIS);
1773
1774        $firstExpression = $this->ScalarExpression();
1775        $this->match(Lexer::T_COMMA);
1776        $secondExpression = $this->ScalarExpression();
1777
1778        $this->match(Lexer::T_CLOSE_PARENTHESIS);
1779
1780        return new AST\NullIfExpression($firstExpression, $secondExpression);
1781    }
1782
1783    /**
1784     * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END"
1785     *
1786     * @return \Doctrine\ORM\Query\AST\GeneralExpression
1787     */
1788    public function GeneralCaseExpression()
1789    {
1790        $this->match(Lexer::T_CASE);
1791
1792        // Process WhenClause (1..N)
1793        $whenClauses = array();
1794
1795        do {
1796            $whenClauses[] = $this->WhenClause();
1797        } while ($this->_lexer->isNextToken(Lexer::T_WHEN));
1798
1799        $this->match(Lexer::T_ELSE);
1800        $scalarExpression = $this->ScalarExpression();
1801        $this->match(Lexer::T_END);
1802
1803        return new AST\GeneralCaseExpression($whenClauses, $scalarExpression);
1804    }
1805
1806    /**
1807     * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END"
1808     * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator
1809     */
1810    public function SimpleCaseExpression()
1811    {
1812        $this->match(Lexer::T_CASE);
1813        $caseOperand = $this->StateFieldPathExpression();
1814
1815        // Process SimpleWhenClause (1..N)
1816        $simpleWhenClauses = array();
1817
1818        do {
1819            $simpleWhenClauses[] = $this->SimpleWhenClause();
1820        } while ($this->_lexer->isNextToken(Lexer::T_WHEN));
1821
1822        $this->match(Lexer::T_ELSE);
1823        $scalarExpression = $this->ScalarExpression();
1824        $this->match(Lexer::T_END);
1825
1826        return new AST\SimpleCaseExpression($caseOperand, $simpleWhenClauses, $scalarExpression);
1827    }
1828
1829    /**
1830     * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression
1831     *
1832     * @return \Doctrine\ORM\Query\AST\WhenExpression
1833     */
1834    public function WhenClause()
1835    {
1836        $this->match(Lexer::T_WHEN);
1837        $conditionalExpression = $this->ConditionalExpression();
1838        $this->match(Lexer::T_THEN);
1839
1840        return new AST\WhenClause($conditionalExpression, $this->ScalarExpression());
1841    }
1842
1843    /**
1844     * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression
1845     *
1846     * @return \Doctrine\ORM\Query\AST\SimpleWhenExpression
1847     */
1848    public function SimpleWhenClause()
1849    {
1850        $this->match(Lexer::T_WHEN);
1851        $conditionalExpression = $this->ScalarExpression();
1852        $this->match(Lexer::T_THEN);
1853
1854        return new AST\SimpleWhenClause($conditionalExpression, $this->ScalarExpression());
1855    }
1856
1857    /**
1858     * SelectExpression ::= (
1859     *     IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration |
1860     *     PartialObjectExpression | "(" Subselect ")" | CaseExpression
1861     * ) [["AS"] ["HIDDEN"] AliasResultVariable]
1862     *
1863     * @return \Doctrine\ORM\Query\AST\SelectExpression
1864     */
1865    public function SelectExpression()
1866    {
1867        $expression    = null;
1868        $identVariable = null;
1869        $peek          = $this->_lexer->glimpse();
1870        $lookaheadType = $this->_lexer->lookahead['type'];
1871
1872        switch (true) {
1873            // ScalarExpression (u.name)
1874            case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] === Lexer::T_DOT):
1875                $expression = $this->ScalarExpression();
1876                break;
1877
1878            // IdentificationVariable (u)
1879            case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] !== Lexer::T_OPEN_PARENTHESIS):
1880                $expression = $identVariable = $this->IdentificationVariable();
1881                break;
1882
1883            // CaseExpression (CASE ... or NULLIF(...) or COALESCE(...))
1884            case ($lookaheadType === Lexer::T_CASE):
1885            case ($lookaheadType === Lexer::T_COALESCE):
1886            case ($lookaheadType === Lexer::T_NULLIF):
1887                $expression = $this->CaseExpression();
1888                break;
1889
1890            // DQL Function (SUM(u.value) or SUM(u.value) + 1)
1891            case ($this->_isFunction()):
1892                $this->_lexer->peek(); // "("
1893
1894                switch (true) {
1895                    case ($this->_isMathOperator($this->_peekBeyondClosingParenthesis())):
1896                        // SUM(u.id) + COUNT(u.id)
1897                        $expression = $this->ScalarExpression();
1898                        break;
1899
1900                    case ($this->_isAggregateFunction($lookaheadType)):
1901                        // COUNT(u.id)
1902                        $expression = $this->AggregateExpression();
1903                        break;
1904
1905                    default:
1906                        // IDENTITY(u)
1907                        $expression = $this->FunctionDeclaration();
1908                        break;
1909                }
1910
1911                break;
1912
1913            // PartialObjectExpression (PARTIAL u.{id, name})
1914            case ($lookaheadType === Lexer::T_PARTIAL):
1915                $expression    = $this->PartialObjectExpression();
1916                $identVariable = $expression->identificationVariable;
1917                break;
1918
1919            // Subselect
1920            case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS && $peek['type'] === Lexer::T_SELECT):
1921                $this->match(Lexer::T_OPEN_PARENTHESIS);
1922                $expression = $this->Subselect();
1923                $this->match(Lexer::T_CLOSE_PARENTHESIS);
1924                break;
1925
1926            // Shortcut: ScalarExpression => SimpleArithmeticExpression
1927            case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS):
1928            case ($lookaheadType === Lexer::T_INTEGER):
1929            case ($lookaheadType === Lexer::T_STRING):
1930            case ($lookaheadType === Lexer::T_FLOAT):
1931            // SimpleArithmeticExpression : (- u.value ) or ( + u.value )
1932            case ($lookaheadType === Lexer::T_MINUS):
1933            case ($lookaheadType === Lexer::T_PLUS):
1934                $expression = $this->SimpleArithmeticExpression();
1935                break;
1936
1937            default:
1938                $this->syntaxError(
1939                    'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression',
1940                    $this->_lexer->lookahead
1941                );
1942        }
1943
1944        // [["AS"] ["HIDDEN"] AliasResultVariable]
1945
1946        if ($this->_lexer->isNextToken(Lexer::T_AS)) {
1947            $this->match(Lexer::T_AS);
1948        }
1949
1950        $hiddenAliasResultVariable = false;
1951
1952        if ($this->_lexer->isNextToken(Lexer::T_HIDDEN)) {
1953            $this->match(Lexer::T_HIDDEN);
1954
1955            $hiddenAliasResultVariable = true;
1956        }
1957
1958        $aliasResultVariable = null;
1959
1960        if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
1961            $token = $this->_lexer->lookahead;
1962            $aliasResultVariable = $this->AliasResultVariable();
1963
1964            // Include AliasResultVariable in query components.
1965            $this->_queryComponents[$aliasResultVariable] = array(
1966                'resultVariable' => $expression,
1967                'nestingLevel'   => $this->_nestingLevel,
1968                'token'          => $token,
1969            );
1970        }
1971
1972        // AST
1973
1974        $expr = new AST\SelectExpression($expression, $aliasResultVariable, $hiddenAliasResultVariable);
1975
1976        if ($identVariable) {
1977            $this->_identVariableExpressions[$identVariable] = $expr;
1978        }
1979
1980        return $expr;
1981    }
1982
1983    /**
1984     * SimpleSelectExpression ::=
1985     *      StateFieldPathExpression | IdentificationVariable |
1986     *      ((AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] AliasResultVariable])
1987     *
1988     * @return \Doctrine\ORM\Query\AST\SimpleSelectExpression
1989     */
1990    public function SimpleSelectExpression()
1991    {
1992        $peek = $this->_lexer->glimpse();
1993
1994        switch ($this->_lexer->lookahead['type']) {
1995            case Lexer::T_IDENTIFIER:
1996                switch (true) {
1997                    case ($peek['type'] === Lexer::T_DOT):
1998                        $expression = $this->StateFieldPathExpression();
1999
2000                        return new AST\SimpleSelectExpression($expression);
2001
2002                    case ($peek['type'] !== Lexer::T_OPEN_PARENTHESIS):
2003                        $expression = $this->IdentificationVariable();
2004
2005                        return new AST\SimpleSelectExpression($expression);
2006
2007                    default:
2008                        // Do nothing
2009                }
2010                break;
2011
2012            case Lexer::T_OPEN_PARENTHESIS:
2013                if ($peek['type'] !== Lexer::T_SELECT) {
2014                    // Shortcut: ScalarExpression => SimpleArithmeticExpression
2015                    $expression = $this->SimpleArithmeticExpression();
2016
2017                    return new AST\SimpleSelectExpression($expression);
2018                }
2019
2020                // Subselect
2021                $this->match(Lexer::T_OPEN_PARENTHESIS);
2022                $expression = $this->Subselect();
2023                $this->match(Lexer::T_CLOSE_PARENTHESIS);
2024
2025                return new AST\SimpleSelectExpression($expression);
2026
2027            default:
2028                // Do nothing
2029        }
2030
2031        $this->_lexer->peek();
2032
2033        $expression = $this->ScalarExpression();
2034        $expr       = new AST\SimpleSelectExpression($expression);
2035
2036        if ($this->_lexer->isNextToken(Lexer::T_AS)) {
2037            $this->match(Lexer::T_AS);
2038        }
2039
2040        if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
2041            $token = $this->_lexer->lookahead;
2042            $resultVariable = $this->AliasResultVariable();
2043            $expr->fieldIdentificationVariable = $resultVariable;
2044
2045            // Include AliasResultVariable in query components.
2046            $this->_queryComponents[$resultVariable] = array(
2047                'resultvariable' => $expr,
2048                'nestingLevel'   => $this->_nestingLevel,
2049                'token'          => $token,
2050            );
2051        }
2052
2053        return $expr;
2054    }
2055
2056    /**
2057     * ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}*
2058     *
2059     * @return \Doctrine\ORM\Query\AST\ConditionalExpression
2060     */
2061    public function ConditionalExpression()
2062    {
2063        $conditionalTerms = array();
2064        $conditionalTerms[] = $this->ConditionalTerm();
2065
2066        while ($this->_lexer->isNextToken(Lexer::T_OR)) {
2067            $this->match(Lexer::T_OR);
2068
2069            $conditionalTerms[] = $this->ConditionalTerm();
2070        }
2071
2072        // Phase 1 AST optimization: Prevent AST\ConditionalExpression
2073        // if only one AST\ConditionalTerm is defined
2074        if (count($conditionalTerms) == 1) {
2075            return $conditionalTerms[0];
2076        }
2077
2078        return new AST\ConditionalExpression($conditionalTerms);
2079    }
2080
2081    /**
2082     * ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}*
2083     *
2084     * @return \Doctrine\ORM\Query\AST\ConditionalTerm
2085     */
2086    public function ConditionalTerm()
2087    {
2088        $conditionalFactors = array();
2089        $conditionalFactors[] = $this->ConditionalFactor();
2090
2091        while ($this->_lexer->isNextToken(Lexer::T_AND)) {
2092            $this->match(Lexer::T_AND);
2093
2094            $conditionalFactors[] = $this->ConditionalFactor();
2095        }
2096
2097        // Phase 1 AST optimization: Prevent AST\ConditionalTerm
2098        // if only one AST\ConditionalFactor is defined
2099        if (count($conditionalFactors) == 1) {
2100            return $conditionalFactors[0];
2101        }
2102
2103        return new AST\ConditionalTerm($conditionalFactors);
2104    }
2105
2106    /**
2107     * ConditionalFactor ::= ["NOT"] ConditionalPrimary
2108     *
2109     * @return \Doctrine\ORM\Query\AST\ConditionalFactor
2110     */
2111    public function ConditionalFactor()
2112    {
2113        $not = false;
2114
2115        if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2116            $this->match(Lexer::T_NOT);
2117
2118            $not = true;
2119        }
2120
2121        $conditionalPrimary = $this->ConditionalPrimary();
2122
2123        // Phase 1 AST optimization: Prevent AST\ConditionalFactor
2124        // if only one AST\ConditionalPrimary is defined
2125        if ( ! $not) {
2126            return $conditionalPrimary;
2127        }
2128
2129        $conditionalFactor = new AST\ConditionalFactor($conditionalPrimary);
2130        $conditionalFactor->not = $not;
2131
2132        return $conditionalFactor;
2133    }
2134
2135    /**
2136     * ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")"
2137     *
2138     * @return \Doctrine\ORM\Query\AST\ConditionalPrimary
2139     */
2140    public function ConditionalPrimary()
2141    {
2142        $condPrimary = new AST\ConditionalPrimary;
2143
2144        if ( ! $this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
2145            $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression();
2146
2147            return $condPrimary;
2148        }
2149
2150        // Peek beyond the matching closing paranthesis ')'
2151        $peek = $this->_peekBeyondClosingParenthesis();
2152
2153        if (in_array($peek['value'], array("=",  "<", "<=", "<>", ">", ">=", "!=")) ||
2154            in_array($peek['type'], array(Lexer::T_NOT, Lexer::T_BETWEEN, Lexer::T_LIKE, Lexer::T_IN, Lexer::T_IS, Lexer::T_EXISTS)) ||
2155            $this->_isMathOperator($peek)) {
2156            $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression();
2157
2158            return $condPrimary;
2159        }
2160
2161        $this->match(Lexer::T_OPEN_PARENTHESIS);
2162        $condPrimary->conditionalExpression = $this->ConditionalExpression();
2163        $this->match(Lexer::T_CLOSE_PARENTHESIS);
2164
2165        return $condPrimary;
2166    }
2167
2168    /**
2169     * SimpleConditionalExpression ::=
2170     *      ComparisonExpression | BetweenExpression | LikeExpression |
2171     *      InExpression | NullComparisonExpression | ExistsExpression |
2172     *      EmptyCollectionComparisonExpression | CollectionMemberExpression |
2173     *      InstanceOfExpression
2174     */
2175    public function SimpleConditionalExpression()
2176    {
2177        $token = $this->_lexer->lookahead;
2178
2179        if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2180            $token = $this->_lexer->glimpse();
2181        }
2182
2183        if ($token['type'] === Lexer::T_EXISTS) {
2184            return $this->ExistsExpression();
2185        }
2186
2187        $peek = $this->_lexer->glimpse();
2188
2189        if ($token['type'] === Lexer::T_IDENTIFIER || $token['type'] === Lexer::T_INPUT_PARAMETER) {
2190            if ($peek['value'] == '(') {
2191                // Peek beyond the matching closing paranthesis ')'
2192                $this->_lexer->peek();
2193                $token = $this->_peekBeyondClosingParenthesis();
2194            } else {
2195                // Peek beyond the PathExpression (or InputParameter)
2196                $peek = $this->_lexer->peek();
2197
2198                while ($peek['value'] === '.') {
2199                    $this->_lexer->peek();
2200                    $peek = $this->_lexer->peek();
2201                }
2202
2203                // Also peek beyond a NOT if there is one
2204                if ($peek['type'] === Lexer::T_NOT) {
2205                    $peek = $this->_lexer->peek();
2206                }
2207
2208                $token = $peek;
2209
2210                // We need to go even further in case of IS (differenciate between NULL and EMPTY)
2211                $lookahead = $this->_lexer->peek();
2212
2213                // Also peek beyond a NOT if there is one
2214                if ($lookahead['type'] === Lexer::T_NOT) {
2215                    $lookahead = $this->_lexer->peek();
2216                }
2217
2218                $this->_lexer->resetPeek();
2219            }
2220        }
2221
2222        switch ($token['type']) {
2223            case Lexer::T_BETWEEN:
2224                return $this->BetweenExpression();
2225            case Lexer::T_LIKE:
2226                return $this->LikeExpression();
2227            case Lexer::T_IN:
2228                return $this->InExpression();
2229            case Lexer::T_INSTANCE:
2230                return $this->InstanceOfExpression();
2231            case Lexer::T_IS:
2232                if ($lookahead['type'] == Lexer::T_NULL) {
2233                    return $this->NullComparisonExpression();
2234                }
2235                return $this->EmptyCollectionComparisonExpression();
2236            case Lexer::T_MEMBER:
2237                return $this->CollectionMemberExpression();
2238            default:
2239                return $this->ComparisonExpression();
2240        }
2241    }
2242
2243    /**
2244     * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY"
2245     *
2246     * @return \Doctrine\ORM\Query\AST\EmptyCollectionComparisonExpression
2247     */
2248    public function EmptyCollectionComparisonExpression()
2249    {
2250        $emptyColletionCompExpr = new AST\EmptyCollectionComparisonExpression(
2251            $this->CollectionValuedPathExpression()
2252        );
2253        $this->match(Lexer::T_IS);
2254
2255        if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2256            $this->match(Lexer::T_NOT);
2257            $emptyColletionCompExpr->not = true;
2258        }
2259
2260        $this->match(Lexer::T_EMPTY);
2261
2262        return $emptyColletionCompExpr;
2263    }
2264
2265    /**
2266     * CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression
2267     *
2268     * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression
2269     * SimpleEntityExpression ::= IdentificationVariable | InputParameter
2270     *
2271     * @return \Doctrine\ORM\Query\AST\CollectionMemberExpression
2272     */
2273    public function CollectionMemberExpression()
2274    {
2275        $not        = false;
2276        $entityExpr = $this->EntityExpression();
2277
2278        if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2279            $this->match(Lexer::T_NOT);
2280
2281            $not = true;
2282        }
2283
2284        $this->match(Lexer::T_MEMBER);
2285
2286        if ($this->_lexer->isNextToken(Lexer::T_OF)) {
2287            $this->match(Lexer::T_OF);
2288        }
2289
2290        $collMemberExpr = new AST\CollectionMemberExpression(
2291            $entityExpr, $this->CollectionValuedPathExpression()
2292        );
2293        $collMemberExpr->not = $not;
2294
2295        return $collMemberExpr;
2296    }
2297
2298    /**
2299     * Literal ::= string | char | integer | float | boolean
2300     *
2301     * @return string
2302     */
2303    public function Literal()
2304    {
2305        switch ($this->_lexer->lookahead['type']) {
2306            case Lexer::T_STRING:
2307                $this->match(Lexer::T_STRING);
2308                return new AST\Literal(AST\Literal::STRING, $this->_lexer->token['value']);
2309
2310            case Lexer::T_INTEGER:
2311            case Lexer::T_FLOAT:
2312                $this->match(
2313                    $this->_lexer->isNextToken(Lexer::T_INTEGER) ? Lexer::T_INTEGER : Lexer::T_FLOAT
2314                );
2315                return new AST\Literal(AST\Literal::NUMERIC, $this->_lexer->token['value']);
2316
2317            case Lexer::T_TRUE:
2318            case Lexer::T_FALSE:
2319                $this->match(
2320                    $this->_lexer->isNextToken(Lexer::T_TRUE) ? Lexer::T_TRUE : Lexer::T_FALSE
2321                );
2322                return new AST\Literal(AST\Literal::BOOLEAN, $this->_lexer->token['value']);
2323
2324            default:
2325                $this->syntaxError('Literal');
2326        }
2327    }
2328
2329    /**
2330     * InParameter ::= Literal | InputParameter
2331     *
2332     * @return string | \Doctrine\ORM\Query\AST\InputParameter
2333     */
2334    public function InParameter()
2335    {
2336        if ($this->_lexer->lookahead['type'] == Lexer::T_INPUT_PARAMETER) {
2337            return $this->inputsParameter();
2338        }
2339
2340        return $this->Literal();
2341    }
2342
2343    /**
2344     * InputParameter ::= PositionalParameter | NamedParameter
2345     *
2346     * @return \Doctrine\ORM\Query\AST\InputParameter
2347     */
2348    public function InputParameter()
2349    {
2350        $this->match(Lexer::T_INPUT_PARAMETER);
2351
2352        return new AST\InputParameter($this->_lexer->token['value']);
2353    }
2354
2355    /**
2356     * ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")"
2357     *
2358     * @return \Doctrine\ORM\Query\AST\ArithmeticExpression
2359     */
2360    public function ArithmeticExpression()
2361    {
2362        $expr = new AST\ArithmeticExpression;
2363
2364        if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
2365            $peek = $this->_lexer->glimpse();
2366
2367            if ($peek['type'] === Lexer::T_SELECT) {
2368                $this->match(Lexer::T_OPEN_PARENTHESIS);
2369                $expr->subselect = $this->Subselect();
2370                $this->match(Lexer::T_CLOSE_PARENTHESIS);
2371
2372                return $expr;
2373            }
2374        }
2375
2376        $expr->simpleArithmeticExpression = $this->SimpleArithmeticExpression();
2377
2378        return $expr;
2379    }
2380
2381    /**
2382     * SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}*
2383     *
2384     * @return \Doctrine\ORM\Query\AST\SimpleArithmeticExpression
2385     */
2386    public function SimpleArithmeticExpression()
2387    {
2388        $terms = array();
2389        $terms[] = $this->ArithmeticTerm();
2390
2391        while (($isPlus = $this->_lexer->isNextToken(Lexer::T_PLUS)) || $this->_lexer->isNextToken(Lexer::T_MINUS)) {
2392            $this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS);
2393
2394            $terms[] = $this->_lexer->token['value'];
2395            $terms[] = $this->ArithmeticTerm();
2396        }
2397
2398        // Phase 1 AST optimization: Prevent AST\SimpleArithmeticExpression
2399        // if only one AST\ArithmeticTerm is defined
2400        if (count($terms) == 1) {
2401            return $terms[0];
2402        }
2403
2404        return new AST\SimpleArithmeticExpression($terms);
2405    }
2406
2407    /**
2408     * ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}*
2409     *
2410     * @return \Doctrine\ORM\Query\AST\ArithmeticTerm
2411     */
2412    public function ArithmeticTerm()
2413    {
2414        $factors = array();
2415        $factors[] = $this->ArithmeticFactor();
2416
2417        while (($isMult = $this->_lexer->isNextToken(Lexer::T_MULTIPLY)) || $this->_lexer->isNextToken(Lexer::T_DIVIDE)) {
2418            $this->match(($isMult) ? Lexer::T_MULTIPLY : Lexer::T_DIVIDE);
2419
2420            $factors[] = $this->_lexer->token['value'];
2421            $factors[] = $this->ArithmeticFactor();
2422        }
2423
2424        // Phase 1 AST optimization: Prevent AST\ArithmeticTerm
2425        // if only one AST\ArithmeticFactor is defined
2426        if (count($factors) == 1) {
2427            return $factors[0];
2428        }
2429
2430        return new AST\ArithmeticTerm($factors);
2431    }
2432
2433    /**
2434     * ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary
2435     *
2436     * @return \Doctrine\ORM\Query\AST\ArithmeticFactor
2437     */
2438    public function ArithmeticFactor()
2439    {
2440        $sign = null;
2441
2442        if (($isPlus = $this->_lexer->isNextToken(Lexer::T_PLUS)) || $this->_lexer->isNextToken(Lexer::T_MINUS)) {
2443            $this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS);
2444            $sign = $isPlus;
2445        }
2446
2447        $primary = $this->ArithmeticPrimary();
2448
2449        // Phase 1 AST optimization: Prevent AST\ArithmeticFactor
2450        // if only one AST\ArithmeticPrimary is defined
2451        if ($sign === null) {
2452            return $primary;
2453        }
2454
2455        return new AST\ArithmeticFactor($primary, $sign);
2456    }
2457
2458    /**
2459     * ArithmeticPrimary ::= SingleValuedPathExpression | Literal | "(" SimpleArithmeticExpression ")"
2460     *          | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings
2461     *          | FunctionsReturningDatetime | IdentificationVariable | ResultVariable | CaseExpression
2462     */
2463    public function ArithmeticPrimary()
2464    {
2465        if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
2466            $this->match(Lexer::T_OPEN_PARENTHESIS);
2467            $expr = $this->SimpleArithmeticExpression();
2468
2469            $this->match(Lexer::T_CLOSE_PARENTHESIS);
2470
2471            return $expr;
2472        }
2473
2474        switch ($this->_lexer->lookahead['type']) {
2475            case Lexer::T_COALESCE:
2476            case Lexer::T_NULLIF:
2477            case Lexer::T_CASE:
2478                return $this->CaseExpression();
2479
2480            case Lexer::T_IDENTIFIER:
2481                $peek = $this->_lexer->glimpse();
2482
2483                if ($peek['value'] == '(') {
2484                    return $this->FunctionDeclaration();
2485                }
2486
2487                if ($peek['value'] == '.') {
2488                    return $this->SingleValuedPathExpression();
2489                }
2490
2491                if (isset($this->_queryComponents[$this->_lexer->lookahead['value']]['resultVariable'])) {
2492                    return $this->ResultVariable();
2493                }
2494
2495                return $this->StateFieldPathExpression();
2496
2497            case Lexer::T_INPUT_PARAMETER:
2498                return $this->inputsParameter();
2499
2500            default:
2501                $peek = $this->_lexer->glimpse();
2502
2503                if ($peek['value'] == '(') {
2504                    if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
2505                        return $this->AggregateExpression();
2506                    }
2507
2508                    return $this->FunctionDeclaration();
2509                }
2510
2511                return $this->Literal();
2512        }
2513    }
2514
2515    /**
2516     * StringExpression ::= StringPrimary | "(" Subselect ")"
2517     *
2518     * @return \Doctrine\ORM\Query\AST\StringPrimary |
2519     *         \Doctrine]ORM\Query\AST\Subselect
2520     */
2521    public function StringExpression()
2522    {
2523        if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
2524            $peek = $this->_lexer->glimpse();
2525
2526            if ($peek['type'] === Lexer::T_SELECT) {
2527                $this->match(Lexer::T_OPEN_PARENTHESIS);
2528                $expr = $this->Subselect();
2529                $this->match(Lexer::T_CLOSE_PARENTHESIS);
2530
2531                return $expr;
2532            }
2533        }
2534
2535        return $this->StringPrimary();
2536    }
2537
2538    /**
2539     * StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression
2540     */
2541    public function StringPrimary()
2542    {
2543        $lookaheadType = $this->_lexer->lookahead['type'];
2544
2545        switch ($lookaheadType) {
2546            case Lexer::T_IDENTIFIER:
2547                $peek = $this->_lexer->glimpse();
2548
2549                if ($peek['value'] == '.') {
2550                    return $this->StateFieldPathExpression();
2551                }
2552
2553                if ($peek['value'] == '(') {
2554                    // do NOT directly go to FunctionsReturningString() because it doesnt check for custom functions.
2555                    return $this->FunctionDeclaration();
2556                }
2557
2558                $this->syntaxError("'.' or '('");
2559                break;
2560
2561            case Lexer::T_STRING:
2562                $this->match(Lexer::T_STRING);
2563
2564                return $this->_lexer->token['value'];
2565
2566            case Lexer::T_INPUT_PARAMETER:
2567                return $this->inputsParameter();
2568
2569            case Lexer::T_CASE:
2570            case Lexer::T_COALESCE:
2571            case Lexer::T_NULLIF:
2572                return $this->CaseExpression();
2573
2574            default:
2575                if ($this->_isAggregateFunction($lookaheadType)) {
2576                    return $this->AggregateExpression();
2577                }
2578        }
2579
2580        $this->syntaxError(
2581            'StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression'
2582        );
2583    }
2584
2585    /**
2586     * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression
2587     *
2588     * @return \Doctrine\ORM\Query\AST\SingleValuedAssociationPathExpression |
2589     *         \Doctrine\ORM\Query\AST\SimpleEntityExpression
2590     */
2591    public function EntityExpression()
2592    {
2593        $glimpse = $this->_lexer->glimpse();
2594
2595        if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER) && $glimpse['value'] === '.') {
2596            return $this->SingleValuedAssociationPathExpression();
2597        }
2598
2599        return $this->SimpleEntityExpression();
2600    }
2601
2602    /**
2603     * SimpleEntityExpression ::= IdentificationVariable | InputParameter
2604     *
2605     * @return string | \Doctrine\ORM\Query\AST\InputParameter
2606     */
2607    public function SimpleEntityExpression()
2608    {
2609        if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
2610            return $this->inputsParameter();
2611        }
2612
2613        return $this->StateFieldPathExpression();
2614    }
2615
2616    /**
2617     * AggregateExpression ::=
2618     *  ("AVG" | "MAX" | "MIN" | "SUM") "(" ["DISTINCT"] StateFieldPathExpression ")" |
2619     *  "COUNT" "(" ["DISTINCT"] (IdentificationVariable | SingleValuedPathExpression) ")"
2620     *
2621     * @return \Doctrine\ORM\Query\AST\AggregateExpression
2622     */
2623    public function AggregateExpression()
2624    {
2625        $lookaheadType = $this->_lexer->lookahead['type'];
2626        $isDistinct = false;
2627
2628        if ( ! in_array($lookaheadType, array(Lexer::T_COUNT, Lexer::T_AVG, Lexer::T_MAX, Lexer::T_MIN, Lexer::T_SUM))) {
2629            $this->syntaxError('One of: MAX, MIN, AVG, SUM, COUNT');
2630        }
2631
2632        $this->match($lookaheadType);
2633        $functionName = $this->_lexer->token['value'];
2634        $this->match(Lexer::T_OPEN_PARENTHESIS);
2635
2636        if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) {
2637            $this->match(Lexer::T_DISTINCT);
2638            $isDistinct = true;
2639        }
2640
2641        $pathExp = ($lookaheadType === Lexer::T_COUNT)
2642            ? $this->SingleValuedPathExpression()
2643            : $this->SimpleArithmeticExpression();
2644
2645            $this->match(Lexer::T_CLOSE_PARENTHESIS);
2646
2647        return new AST\AggregateExpression($functionName, $pathExp, $isDistinct);
2648    }
2649
2650    /**
2651     * QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")"
2652     *
2653     * @return \Doctrine\ORM\Query\AST\QuantifiedExpression
2654     */
2655    public function QuantifiedExpression()
2656    {
2657        $lookaheadType = $this->_lexer->lookahead['type'];
2658        $value = $this->_lexer->lookahead['value'];
2659
2660        if ( ! in_array($lookaheadType, array(Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME))) {
2661            $this->syntaxError('ALL, ANY or SOME');
2662        }
2663
2664        $this->match($lookaheadType);
2665        $this->match(Lexer::T_OPEN_PARENTHESIS);
2666
2667        $qExpr = new AST\QuantifiedExpression($this->Subselect());
2668        $qExpr->type = $value;
2669
2670        $this->match(Lexer::T_CLOSE_PARENTHESIS);
2671
2672        return $qExpr;
2673    }
2674
2675    /**
2676     * BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression
2677     *
2678     * @return \Doctrine\ORM\Query\AST\BetweenExpression
2679     */
2680    public function BetweenExpression()
2681    {
2682        $not = false;
2683        $arithExpr1 = $this->ArithmeticExpression();
2684
2685        if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2686            $this->match(Lexer::T_NOT);
2687            $not = true;
2688        }
2689
2690        $this->match(Lexer::T_BETWEEN);
2691        $arithExpr2 = $this->ArithmeticExpression();
2692        $this->match(Lexer::T_AND);
2693        $arithExpr3 = $this->ArithmeticExpression();
2694
2695        $betweenExpr = new AST\BetweenExpression($arithExpr1, $arithExpr2, $arithExpr3);
2696        $betweenExpr->not = $not;
2697
2698        return $betweenExpr;
2699    }
2700
2701    /**
2702     * ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression )
2703     *
2704     * @return \Doctrine\ORM\Query\AST\ComparisonExpression
2705     */
2706    public function ComparisonExpression()
2707    {
2708        $peek = $this->_lexer->glimpse();
2709
2710        $leftExpr  = $this->ArithmeticExpression();
2711        $operator  = $this->ComparisonOperator();
2712        $rightExpr = ($this->_isNextAllAnySome())
2713            ? $this->QuantifiedExpression()
2714            : $this->ArithmeticExpression();
2715
2716        return new AST\ComparisonExpression($leftExpr, $operator, $rightExpr);
2717    }
2718
2719    /**
2720     * InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
2721     *
2722     * @return \Doctrine\ORM\Query\AST\InExpression
2723     */
2724    public function InExpression()
2725    {
2726        $inExpression = new AST\InExpression($this->ArithmeticExpression());
2727
2728        if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2729            $this->match(Lexer::T_NOT);
2730            $inExpression->not = true;
2731        }
2732
2733        $this->match(Lexer::T_IN);
2734        $this->match(Lexer::T_OPEN_PARENTHESIS);
2735
2736        if ($this->_lexer->isNextToken(Lexer::T_SELECT)) {
2737            $inExpression->subselect = $this->Subselect();
2738        } else {
2739            $literals = array();
2740            $literals[] = $this->InParameter();
2741
2742            while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
2743                $this->match(Lexer::T_COMMA);
2744                $literals[] = $this->InParameter();
2745            }
2746
2747            $inExpression->literals = $literals;
2748        }
2749
2750        $this->match(Lexer::T_CLOSE_PARENTHESIS);
2751
2752        return $inExpression;
2753    }
2754
2755    /**
2756     * InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")")
2757     *
2758     * @return \Doctrine\ORM\Query\AST\InstanceOfExpression
2759     */
2760    public function InstanceOfExpression()
2761    {
2762        $instanceOfExpression = new AST\InstanceOfExpression($this->IdentificationVariable());
2763
2764        if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2765            $this->match(Lexer::T_NOT);
2766            $instanceOfExpression->not = true;
2767        }
2768
2769        $this->match(Lexer::T_INSTANCE);
2770        $this->match(Lexer::T_OF);
2771
2772        $exprValues = array();
2773
2774        if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
2775            $this->match(Lexer::T_OPEN_PARENTHESIS);
2776
2777            $exprValues[] = $this->InstanceOfParameter();
2778
2779            while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
2780                $this->match(Lexer::T_COMMA);
2781
2782                $exprValues[] = $this->InstanceOfParameter();
2783            }
2784
2785            $this->match(Lexer::T_CLOSE_PARENTHESIS);
2786
2787            $instanceOfExpression->value = $exprValues;
2788
2789            return $instanceOfExpression;
2790        }
2791
2792        $exprValues[] = $this->InstanceOfParameter();
2793
2794        $instanceOfExpression->value = $exprValues;
2795
2796        return $instanceOfExpression;
2797    }
2798
2799    /**
2800     * InstanceOfParameter ::= AbstractSchemaName | InputParameter
2801     *
2802     * @return mixed
2803     */
2804    public function InstanceOfParameter()
2805    {
2806        if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
2807            $this->match(Lexer::T_INPUT_PARAMETER);
2808
2809            return new AST\InputParameter($this->_lexer->token['value']);
2810        }
2811
2812        return $this->AliasIdentificationVariable();
2813    }
2814
2815    /**
2816     * LikeExpression ::= StringExpression ["NOT"] "LIKE" (string | input_parameter) ["ESCAPE" char]
2817     *
2818     * @return \Doctrine\ORM\Query\AST\LikeExpression
2819     */
2820    public function LikeExpression()
2821    {
2822        $stringExpr = $this->StringExpression();
2823        $not = false;
2824
2825        if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2826            $this->match(Lexer::T_NOT);
2827            $not = true;
2828        }
2829
2830        $this->match(Lexer::T_LIKE);
2831
2832        if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
2833            $this->match(Lexer::T_INPUT_PARAMETER);
2834            $stringPattern = new AST\InputParameter($this->_lexer->token['value']);
2835        } else {
2836            $this->match(Lexer::T_STRING);
2837            $stringPattern = $this->_lexer->token['value'];
2838        }
2839
2840        $escapeChar = null;
2841
2842        if ($this->_lexer->lookahead['type'] === Lexer::T_ESCAPE) {
2843            $this->match(Lexer::T_ESCAPE);
2844            $this->match(Lexer::T_STRING);
2845            $escapeChar = $this->_lexer->token['value'];
2846        }
2847
2848        $likeExpr = new AST\LikeExpression($stringExpr, $stringPattern, $escapeChar);
2849        $likeExpr->not = $not;
2850
2851        return $likeExpr;
2852    }
2853
2854    /**
2855     * NullComparisonExpression ::= (SingleValuedPathExpression | InputParameter) "IS" ["NOT"] "NULL"
2856     *
2857     * @return \Doctrine\ORM\Query\AST\NullComparisonExpression
2858     */
2859    public function NullComparisonExpression()
2860    {
2861        if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
2862            $this->match(Lexer::T_INPUT_PARAMETER);
2863            $expr = new AST\InputParameter($this->_lexer->token['value']);
2864        } else {
2865            $expr = $this->SingleValuedPathExpression();
2866        }
2867
2868        $nullCompExpr = new AST\NullComparisonExpression($expr);
2869        $this->match(Lexer::T_IS);
2870
2871        if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2872            $this->match(Lexer::T_NOT);
2873            $nullCompExpr->not = true;
2874        }
2875
2876        $this->match(Lexer::T_NULL);
2877
2878        return $nullCompExpr;
2879    }
2880
2881    /**
2882     * ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")"
2883     *
2884     * @return \Doctrine\ORM\Query\AST\ExistsExpression
2885     */
2886    public function ExistsExpression()
2887    {
2888        $not = false;
2889
2890        if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
2891            $this->match(Lexer::T_NOT);
2892            $not = true;
2893        }
2894
2895        $this->match(Lexer::T_EXISTS);
2896        $this->match(Lexer::T_OPEN_PARENTHESIS);
2897
2898        $existsExpression = new AST\ExistsExpression($this->Subselect());
2899        $existsExpression->not = $not;
2900
2901        $this->match(Lexer::T_CLOSE_PARENTHESIS);
2902
2903        return $existsExpression;
2904    }
2905
2906    /**
2907     * ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!="
2908     *
2909     * @return string
2910     */
2911    public function ComparisonOperator()
2912    {
2913        switch ($this->_lexer->lookahead['value']) {
2914            case '=':
2915                $this->match(Lexer::T_EQUALS);
2916
2917                return '=';
2918
2919            case '<':
2920                $this->match(Lexer::T_LOWER_THAN);
2921                $operator = '<';
2922
2923                if ($this->_lexer->isNextToken(Lexer::T_EQUALS)) {
2924                    $this->match(Lexer::T_EQUALS);
2925                    $operator .= '=';
2926                } else if ($this->_lexer->isNextToken(Lexer::T_GREATER_THAN)) {
2927                    $this->match(Lexer::T_GREATER_THAN);
2928                    $operator .= '>';
2929                }
2930
2931                return $operator;
2932
2933            case '>':
2934                $this->match(Lexer::T_GREATER_THAN);
2935                $operator = '>';
2936
2937                if ($this->_lexer->isNextToken(Lexer::T_EQUALS)) {
2938                    $this->match(Lexer::T_EQUALS);
2939                    $operator .= '=';
2940                }
2941
2942                return $operator;
2943
2944            case '!':
2945                $this->match(Lexer::T_NEGATE);
2946                $this->match(Lexer::T_EQUALS);
2947
2948                return '<>';
2949
2950            default:
2951                $this->syntaxError('=, <, <=, <>, >, >=, !=');
2952        }
2953    }
2954
2955    /**
2956     * FunctionDeclaration ::= FunctionsReturningStrings | FunctionsReturningNumerics | FunctionsReturningDatetime
2957     */
2958    public function FunctionDeclaration()
2959    {
2960        $token = $this->_lexer->lookahead;
2961        $funcName = strtolower($token['value']);
2962
2963        // Check for built-in functions first!
2964        switch (true) {
2965            case (isset(self::$_STRING_FUNCTIONS[$funcName])):
2966                return $this->FunctionsReturningStrings();
2967
2968            case (isset(self::$_NUMERIC_FUNCTIONS[$funcName])):
2969                return $this->FunctionsReturningNumerics();
2970
2971            case (isset(self::$_DATETIME_FUNCTIONS[$funcName])):
2972                return $this->FunctionsReturningDatetime();
2973
2974            default:
2975                return $this->CustomFunctionDeclaration();
2976        }
2977    }
2978
2979    /**
2980     * Helper function for FunctionDeclaration grammar rule
2981     */
2982    private function CustomFunctionDeclaration()
2983    {
2984        $token = $this->_lexer->lookahead;
2985        $funcName = strtolower($token['value']);
2986
2987        // Check for custom functions afterwards
2988        $config = $this->_em->getConfiguration();
2989
2990        switch (true) {
2991            case ($config->getCustomStringFunction($funcName) !== null):
2992                return $this->CustomFunctionsReturningStrings();
2993
2994            case ($config->getCustomNumericFunction($funcName) !== null):
2995                return $this->CustomFunctionsReturningNumerics();
2996
2997            case ($config->getCustomDatetimeFunction($funcName) !== null):
2998                return $this->CustomFunctionsReturningDatetime();
2999
3000            default:
3001                $this->syntaxError('known function', $token);
3002        }
3003    }
3004
3005    /**
3006     * FunctionsReturningNumerics ::=
3007     *      "LENGTH" "(" StringPrimary ")" |
3008     *      "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" |
3009     *      "ABS" "(" SimpleArithmeticExpression ")" |
3010     *      "SQRT" "(" SimpleArithmeticExpression ")" |
3011     *      "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" |
3012     *      "SIZE" "(" CollectionValuedPathExpression ")"
3013     */
3014    public function FunctionsReturningNumerics()
3015    {
3016        $funcNameLower = strtolower($this->_lexer->lookahead['value']);
3017        $funcClass     = self::$_NUMERIC_FUNCTIONS[$funcNameLower];
3018
3019        $function = new $funcClass($funcNameLower);
3020        $function->parse($this);
3021
3022        return $function;
3023    }
3024
3025    public function CustomFunctionsReturningNumerics()
3026    {
3027        // getCustomNumericFunction is case-insensitive
3028        $funcName  = strtolower($this->_lexer->lookahead['value']);
3029        $funcClass = $this->_em->getConfiguration()->getCustomNumericFunction($funcName);
3030
3031        $function = new $funcClass($funcName);
3032        $function->parse($this);
3033
3034        return $function;
3035    }
3036
3037    /**
3038     * FunctionsReturningDateTime ::= "CURRENT_DATE" | "CURRENT_TIME" | "CURRENT_TIMESTAMP"
3039     */
3040    public function FunctionsReturningDatetime()
3041    {
3042        $funcNameLower = strtolower($this->_lexer->lookahead['value']);
3043        $funcClass     = self::$_DATETIME_FUNCTIONS[$funcNameLower];
3044
3045        $function = new $funcClass($funcNameLower);
3046        $function->parse($this);
3047
3048        return $function;
3049    }
3050
3051    public function CustomFunctionsReturningDatetime()
3052    {
3053        // getCustomDatetimeFunction is case-insensitive
3054        $funcName  = $this->_lexer->lookahead['value'];
3055        $funcClass = $this->_em->getConfiguration()->getCustomDatetimeFunction($funcName);
3056
3057        $function = new $funcClass($funcName);
3058        $function->parse($this);
3059
3060        return $function;
3061    }
3062
3063    /**
3064     * FunctionsReturningStrings ::=
3065     *   "CONCAT" "(" StringPrimary "," StringPrimary ")" |
3066     *   "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" |
3067     *   "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" |
3068     *   "LOWER" "(" StringPrimary ")" |
3069     *   "UPPER" "(" StringPrimary ")"
3070     */
3071    public function FunctionsReturningStrings()
3072    {
3073        $funcNameLower = strtolower($this->_lexer->lookahead['value']);
3074        $funcClass     = self::$_STRING_FUNCTIONS[$funcNameLower];
3075
3076        $function = new $funcClass($funcNameLower);
3077        $function->parse($this);
3078
3079        return $function;
3080    }
3081
3082    public function CustomFunctionsReturningStrings()
3083    {
3084        // getCustomStringFunction is case-insensitive
3085        $funcName  = $this->_lexer->lookahead['value'];
3086        $funcClass = $this->_em->getConfiguration()->getCustomStringFunction($funcName);
3087
3088        $function = new $funcClass($funcName);
3089        $function->parse($this);
3090
3091        return $function;
3092    }
3093}
Note: See TracBrowser for help on using the repository browser.