Completed
Push — master ( fc67b3...ad5014 )
by Luís
21s
created

lib/Doctrine/ORM/Query/Parser.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM\Query;
21
22
use Doctrine\ORM\Mapping\ClassMetadata;
23
use Doctrine\ORM\Query;
24
use Doctrine\ORM\Query\AST\Functions;
25
26
/**
27
 * An LL(*) recursive-descent parser for the context-free grammar of the Doctrine Query Language.
28
 * Parses a DQL query, reports any errors in it, and generates an AST.
29
 *
30
 * @since   2.0
31
 * @author  Guilherme Blanco <[email protected]>
32
 * @author  Jonathan Wage <[email protected]>
33
 * @author  Roman Borschel <[email protected]>
34
 * @author  Janne Vanhala <[email protected]>
35
 * @author  Fabio B. Silva <[email protected]>
36
 */
37
class Parser
38
{
39
    /**
40
     * READ-ONLY: Maps BUILT-IN string function names to AST class names.
41
     *
42
     * @var array
43
     */
44
    private static $_STRING_FUNCTIONS = [
45
        'concat'    => Functions\ConcatFunction::class,
46
        'substring' => Functions\SubstringFunction::class,
47
        'trim'      => Functions\TrimFunction::class,
48
        'lower'     => Functions\LowerFunction::class,
49
        'upper'     => Functions\UpperFunction::class,
50
        'identity'  => Functions\IdentityFunction::class,
51
    ];
52
53
    /**
54
     * READ-ONLY: Maps BUILT-IN numeric function names to AST class names.
55
     *
56
     * @var array
57
     */
58
    private static $_NUMERIC_FUNCTIONS = [
59
        'length'    => Functions\LengthFunction::class,
60
        'locate'    => Functions\LocateFunction::class,
61
        'abs'       => Functions\AbsFunction::class,
62
        'sqrt'      => Functions\SqrtFunction::class,
63
        'mod'       => Functions\ModFunction::class,
64
        'size'      => Functions\SizeFunction::class,
65
        'date_diff' => Functions\DateDiffFunction::class,
66
        'bit_and'   => Functions\BitAndFunction::class,
67
        'bit_or'    => Functions\BitOrFunction::class,
68
    ];
69
70
    /**
71
     * READ-ONLY: Maps BUILT-IN datetime function names to AST class names.
72
     *
73
     * @var array
74
     */
75
    private static $_DATETIME_FUNCTIONS = [
76
        'current_date'      => Functions\CurrentDateFunction::class,
77
        'current_time'      => Functions\CurrentTimeFunction::class,
78
        'current_timestamp' => Functions\CurrentTimestampFunction::class,
79
        'date_add'          => Functions\DateAddFunction::class,
80
        'date_sub'          => Functions\DateSubFunction::class,
81
    ];
82
83
    /*
84
     * Expressions that were encountered during parsing of identifiers and expressions
85
     * and still need to be validated.
86
     */
87
88
    /**
89
     * @var array
90
     */
91
    private $deferredIdentificationVariables = [];
92
93
    /**
94
     * @var array
95
     */
96
    private $deferredPartialObjectExpressions = [];
97
98
    /**
99
     * @var array
100
     */
101
    private $deferredPathExpressions = [];
102
103
    /**
104
     * @var array
105
     */
106
    private $deferredResultVariables = [];
107
108
    /**
109
     * @var array
110
     */
111
    private $deferredNewObjectExpressions = [];
112
113
    /**
114
     * The lexer.
115
     *
116
     * @var \Doctrine\ORM\Query\Lexer
117
     */
118
    private $lexer;
119
120
    /**
121
     * The parser result.
122
     *
123
     * @var \Doctrine\ORM\Query\ParserResult
124
     */
125
    private $parserResult;
126
127
    /**
128
     * The EntityManager.
129
     *
130
     * @var \Doctrine\ORM\EntityManager
131
     */
132
    private $em;
133
134
    /**
135
     * The Query to parse.
136
     *
137
     * @var Query
138
     */
139
    private $query;
140
141
    /**
142
     * Map of declared query components in the parsed query.
143
     *
144
     * @var array
145
     */
146
    private $queryComponents = [];
147
148
    /**
149
     * Keeps the nesting level of defined ResultVariables.
150
     *
151
     * @var integer
152
     */
153
    private $nestingLevel = 0;
154
155
    /**
156
     * Any additional custom tree walkers that modify the AST.
157
     *
158
     * @var array
159
     */
160
    private $customTreeWalkers = [];
161
162
    /**
163
     * The custom last tree walker, if any, that is responsible for producing the output.
164
     *
165
     * @var TreeWalker
166
     */
167
    private $customOutputWalker;
168
169
    /**
170
     * @var array
171
     */
172
    private $identVariableExpressions = [];
173
174
    /**
175
     * Checks if a function is internally defined. Used to prevent overwriting
176
     * of built-in functions through user-defined functions.
177
     *
178
     * @param string $functionName
179
     *
180
     * @return bool
181
     */
182 6
    static public function isInternalFunction($functionName)
183
    {
184 6
        $functionName = strtolower($functionName);
185
186 6
        return isset(self::$_STRING_FUNCTIONS[$functionName])
187 6
            || isset(self::$_DATETIME_FUNCTIONS[$functionName])
188 6
            || isset(self::$_NUMERIC_FUNCTIONS[$functionName]);
189
    }
190
191
    /**
192
     * Creates a new query parser object.
193
     *
194
     * @param Query $query The Query to parse.
195
     */
196 820
    public function __construct(Query $query)
197
    {
198 820
        $this->query        = $query;
199 820
        $this->em           = $query->getEntityManager();
200 820
        $this->lexer        = new Lexer($query->getDQL());
201 820
        $this->parserResult = new ParserResult();
202 820
    }
203
204
    /**
205
     * Sets a custom tree walker that produces output.
206
     * This tree walker will be run last over the AST, after any other walkers.
207
     *
208
     * @param string $className
209
     *
210
     * @return void
211
     */
212 127
    public function setCustomOutputTreeWalker($className)
213
    {
214 127
        $this->customOutputWalker = $className;
215 127
    }
216
217
    /**
218
     * Adds a custom tree walker for modifying the AST.
219
     *
220
     * @param string $className
221
     *
222
     * @return void
223
     */
224
    public function addCustomTreeWalker($className)
225
    {
226
        $this->customTreeWalkers[] = $className;
227
    }
228
229
    /**
230
     * Gets the lexer used by the parser.
231
     *
232
     * @return \Doctrine\ORM\Query\Lexer
233
     */
234 28
    public function getLexer()
235
    {
236 28
        return $this->lexer;
237
    }
238
239
    /**
240
     * Gets the ParserResult that is being filled with information during parsing.
241
     *
242
     * @return \Doctrine\ORM\Query\ParserResult
243
     */
244
    public function getParserResult()
245
    {
246
        return $this->parserResult;
247
    }
248
249
    /**
250
     * Gets the EntityManager used by the parser.
251
     *
252
     * @return \Doctrine\ORM\EntityManager
253
     */
254
    public function getEntityManager()
255
    {
256
        return $this->em;
257
    }
258
259
    /**
260
     * Parses and builds AST for the given Query.
261
     *
262
     * @return \Doctrine\ORM\Query\AST\SelectStatement |
263
     *         \Doctrine\ORM\Query\AST\UpdateStatement |
264
     *         \Doctrine\ORM\Query\AST\DeleteStatement
265
     */
266 820
    public function getAST()
267
    {
268
        // Parse & build AST
269 820
        $AST = $this->QueryLanguage();
270
271
        // Process any deferred validations of some nodes in the AST.
272
        // This also allows post-processing of the AST for modification purposes.
273 781
        $this->processDeferredIdentificationVariables();
274
275 779
        if ($this->deferredPartialObjectExpressions) {
276 11
            $this->processDeferredPartialObjectExpressions();
277
        }
278
279 777
        if ($this->deferredPathExpressions) {
280 584
            $this->processDeferredPathExpressions();
281
        }
282
283 774
        if ($this->deferredResultVariables) {
284 30
            $this->processDeferredResultVariables();
285
        }
286
287 774
        if ($this->deferredNewObjectExpressions) {
288 28
            $this->processDeferredNewObjectExpressions($AST);
289
        }
290
291 770
        $this->processRootEntityAliasSelected();
292
293
        // TODO: Is there a way to remove this? It may impact the mixed hydration resultset a lot!
294 769
        $this->fixIdentificationVariableOrder($AST);
295
296 769
        return $AST;
297
    }
298
299
    /**
300
     * Attempts to match the given token with the current lookahead token.
301
     *
302
     * If they match, updates the lookahead token; otherwise raises a syntax
303
     * error.
304
     *
305
     * @param int $token The token type.
306
     *
307
     * @return void
308
     *
309
     * @throws QueryException If the tokens don't match.
310
     */
311 831
    public function match($token)
312
    {
313 831
        $lookaheadType = $this->lexer->lookahead['type'];
314
315
        // Short-circuit on first condition, usually types match
316 831
        if ($lookaheadType !== $token) {
317
            // If parameter is not identifier (1-99) must be exact match
318 20
            if ($token < Lexer::T_IDENTIFIER) {
319 2
                $this->syntaxError($this->lexer->getLiteral($token));
320
            }
321
322
            // If parameter is keyword (200+) must be exact match
323 18
            if ($token > Lexer::T_IDENTIFIER) {
324 7
                $this->syntaxError($this->lexer->getLiteral($token));
325
            }
326
327
            // If parameter is T_IDENTIFIER, then matches T_IDENTIFIER (100) and keywords (200+)
328 11
            if ($token === Lexer::T_IDENTIFIER && $lookaheadType < Lexer::T_IDENTIFIER) {
329 8
                $this->syntaxError($this->lexer->getLiteral($token));
330
            }
331
        }
332
333 824
        $this->lexer->moveNext();
334 824
    }
335
336
    /**
337
     * Frees this parser, enabling it to be reused.
338
     *
339
     * @param boolean $deep     Whether to clean peek and reset errors.
340
     * @param integer $position Position to reset.
341
     *
342
     * @return void
343
     */
344
    public function free($deep = false, $position = 0)
345
    {
346
        // WARNING! Use this method with care. It resets the scanner!
347
        $this->lexer->resetPosition($position);
348
349
        // Deep = true cleans peek and also any previously defined errors
350
        if ($deep) {
351
            $this->lexer->resetPeek();
352
        }
353
354
        $this->lexer->token = null;
355
        $this->lexer->lookahead = null;
356
    }
357
358
    /**
359
     * Parses a query string.
360
     *
361
     * @return ParserResult
362
     */
363 820
    public function parse()
364
    {
365 820
        $AST = $this->getAST();
366
367 769
        if (($customWalkers = $this->query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) !== false) {
368 93
            $this->customTreeWalkers = $customWalkers;
369
        }
370
371 769
        if (($customOutputWalker = $this->query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER)) !== false) {
372 78
            $this->customOutputWalker = $customOutputWalker;
373
        }
374
375
        // Run any custom tree walkers over the AST
376 769
        if ($this->customTreeWalkers) {
377 92
            $treeWalkerChain = new TreeWalkerChain($this->query, $this->parserResult, $this->queryComponents);
378
379 92
            foreach ($this->customTreeWalkers as $walker) {
380 92
                $treeWalkerChain->addTreeWalker($walker);
381
            }
382
383
            switch (true) {
384 92
                case ($AST instanceof AST\UpdateStatement):
385
                    $treeWalkerChain->walkUpdateStatement($AST);
386
                    break;
387
388 92
                case ($AST instanceof AST\DeleteStatement):
389
                    $treeWalkerChain->walkDeleteStatement($AST);
390
                    break;
391
392 92
                case ($AST instanceof AST\SelectStatement):
393
                default:
394 92
                    $treeWalkerChain->walkSelectStatement($AST);
395
            }
396
397 86
            $this->queryComponents = $treeWalkerChain->getQueryComponents();
398
        }
399
400 763
        $outputWalkerClass = $this->customOutputWalker ?: SqlWalker::class;
401 763
        $outputWalker      = new $outputWalkerClass($this->query, $this->parserResult, $this->queryComponents);
402
403
        // Assign an SQL executor to the parser result
404 763
        $this->parserResult->setSqlExecutor($outputWalker->getExecutor($AST));
405
406 754
        return $this->parserResult;
407
    }
408
409
    /**
410
     * Fixes order of identification variables.
411
     *
412
     * They have to appear in the select clause in the same order as the
413
     * declarations (from ... x join ... y join ... z ...) appear in the query
414
     * as the hydration process relies on that order for proper operation.
415
     *
416
     * @param AST\SelectStatement|AST\DeleteStatement|AST\UpdateStatement $AST
417
     *
418
     * @return void
419
     */
420 769
    private function fixIdentificationVariableOrder($AST)
421
    {
422 769
        if (count($this->identVariableExpressions) <= 1) {
423 594
            return;
424
        }
425
426 180
        foreach ($this->queryComponents as $dqlAlias => $qComp) {
427 180
            if ( ! isset($this->identVariableExpressions[$dqlAlias])) {
428 8
                continue;
429
            }
430
431 180
            $expr = $this->identVariableExpressions[$dqlAlias];
432 180
            $key  = array_search($expr, $AST->selectClause->selectExpressions);
433
434 180
            unset($AST->selectClause->selectExpressions[$key]);
435
436 180
            $AST->selectClause->selectExpressions[] = $expr;
437
        }
438 180
    }
439
440
    /**
441
     * Generates a new syntax error.
442
     *
443
     * @param string     $expected Expected string.
444
     * @param array|null $token    Got token.
445
     *
446
     * @return void
447
     *
448
     * @throws \Doctrine\ORM\Query\QueryException
449
     */
450 17
    public function syntaxError($expected = '', $token = null)
451
    {
452 17
        if ($token === null) {
453 14
            $token = $this->lexer->lookahead;
454
        }
455
456 17
        $tokenPos = (isset($token['position'])) ? $token['position'] : '-1';
457
458 17
        $message  = "line 0, col {$tokenPos}: Error: ";
459 17
        $message .= ($expected !== '') ? "Expected {$expected}, got " : 'Unexpected ';
460 17
        $message .= ($this->lexer->lookahead === null) ? 'end of string.' : "'{$token['value']}'";
461
462 17
        throw QueryException::syntaxError($message, QueryException::dqlError($this->query->getDQL()));
463
    }
464
465
    /**
466
     * Generates a new semantical error.
467
     *
468
     * @param string     $message Optional message.
469
     * @param array|null $token   Optional token.
470
     *
471
     * @return void
472
     *
473
     * @throws \Doctrine\ORM\Query\QueryException
474
     */
475 33
    public function semanticalError($message = '', $token = null)
476
    {
477 33
        if ($token === null) {
478 2
            $token = $this->lexer->lookahead;
479
        }
480
481
        // Minimum exposed chars ahead of token
482 33
        $distance = 12;
483
484
        // Find a position of a final word to display in error string
485 33
        $dql    = $this->query->getDQL();
486 33
        $length = strlen($dql);
487 33
        $pos    = $token['position'] + $distance;
488 33
        $pos    = strpos($dql, ' ', ($length > $pos) ? $pos : $length);
489 33
        $length = ($pos !== false) ? $pos - $token['position'] : $distance;
490
491 33
        $tokenPos = (isset($token['position']) && $token['position'] > 0) ? $token['position'] : '-1';
492 33
        $tokenStr = substr($dql, $token['position'], $length);
493
494
        // Building informative message
495 33
        $message = 'line 0, col ' . $tokenPos . " near '" . $tokenStr . "': Error: " . $message;
496
497 33
        throw QueryException::semanticalError($message, QueryException::dqlError($this->query->getDQL()));
498
    }
499
500
    /**
501
     * Peeks beyond the matched closing parenthesis and returns the first token after that one.
502
     *
503
     * @param boolean $resetPeek Reset peek after finding the closing parenthesis.
504
     *
505
     * @return array
506
     */
507 156
    private function peekBeyondClosingParenthesis($resetPeek = true)
508
    {
509 156
        $token = $this->lexer->peek();
510 156
        $numUnmatched = 1;
511
512 156
        while ($numUnmatched > 0 && $token !== null) {
513 155
            switch ($token['type']) {
514 155
                case Lexer::T_OPEN_PARENTHESIS:
515 33
                    ++$numUnmatched;
516 33
                    break;
517
518 155
                case Lexer::T_CLOSE_PARENTHESIS:
519 155
                    --$numUnmatched;
520 155
                    break;
521
522
                default:
523
                    // Do nothing
524
            }
525
526 155
            $token = $this->lexer->peek();
527
        }
528
529 156
        if ($resetPeek) {
530 135
            $this->lexer->resetPeek();
531
        }
532
533 156
        return $token;
534
    }
535
536
    /**
537
     * Checks if the given token indicates a mathematical operator.
538
     *
539
     * @param array $token
540
     *
541
     * @return boolean TRUE if the token is a mathematical operator, FALSE otherwise.
542
     */
543 343
    private function isMathOperator($token)
544
    {
545 343
        return in_array($token['type'], [Lexer::T_PLUS, Lexer::T_MINUS, Lexer::T_DIVIDE, Lexer::T_MULTIPLY]);
546
    }
547
548
    /**
549
     * Checks if the next-next (after lookahead) token starts a function.
550
     *
551
     * @return boolean TRUE if the next-next tokens start a function, FALSE otherwise.
552
     */
553 388
    private function isFunction()
554
    {
555 388
        $lookaheadType = $this->lexer->lookahead['type'];
556 388
        $peek          = $this->lexer->peek();
557
558 388
        $this->lexer->resetPeek();
559
560 388
        return ($lookaheadType >= Lexer::T_IDENTIFIER && $peek['type'] === Lexer::T_OPEN_PARENTHESIS);
561
    }
562
563
    /**
564
     * Checks whether the given token type indicates an aggregate function.
565
     *
566
     * @param int $tokenType
567
     *
568
     * @return boolean TRUE if the token type is an aggregate function, FALSE otherwise.
569
     */
570 126
    private function isAggregateFunction($tokenType)
571
    {
572 126
        return in_array($tokenType, [Lexer::T_AVG, Lexer::T_MIN, Lexer::T_MAX, Lexer::T_SUM, Lexer::T_COUNT]);
573
    }
574
575
    /**
576
     * Checks whether the current lookahead token of the lexer has the type T_ALL, T_ANY or T_SOME.
577
     *
578
     * @return boolean
579
     */
580 293
    private function isNextAllAnySome()
581
    {
582 293
        return in_array($this->lexer->lookahead['type'], [Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME]);
583
    }
584
585
    /**
586
     * Validates that the given <tt>IdentificationVariable</tt> is semantically correct.
587
     * It must exist in query components list.
588
     *
589
     * @return void
590
     */
591 781
    private function processDeferredIdentificationVariables()
592
    {
593 781
        foreach ($this->deferredIdentificationVariables as $deferredItem) {
594 769
            $identVariable = $deferredItem['expression'];
595
596
            // Check if IdentificationVariable exists in queryComponents
597 769
            if ( ! isset($this->queryComponents[$identVariable])) {
598 1
                $this->semanticalError(
599 1
                    "'$identVariable' is not defined.", $deferredItem['token']
600
                );
601
            }
602
603 769
            $qComp = $this->queryComponents[$identVariable];
604
605
            // Check if queryComponent points to an AbstractSchemaName or a ResultVariable
606 769
            if ( ! isset($qComp['metadata'])) {
607
                $this->semanticalError(
608
                    "'$identVariable' does not point to a Class.", $deferredItem['token']
609
                );
610
            }
611
612
            // Validate if identification variable nesting level is lower or equal than the current one
613 769
            if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) {
614 1
                $this->semanticalError(
615 769
                    "'$identVariable' is used outside the scope of its declaration.", $deferredItem['token']
616
                );
617
            }
618
        }
619 779
    }
620
621
    /**
622
     * Validates that the given <tt>NewObjectExpression</tt>.
623
     *
624
     * @param \Doctrine\ORM\Query\AST\SelectClause $AST
625
     *
626
     * @return void
627
     */
628 28
    private function processDeferredNewObjectExpressions($AST)
629
    {
630 28
        foreach ($this->deferredNewObjectExpressions as $deferredItem) {
631 28
            $expression     = $deferredItem['expression'];
632 28
            $token          = $deferredItem['token'];
633 28
            $className      = $expression->className;
634 28
            $args           = $expression->args;
635 28
            $fromClassName  = isset($AST->fromClause->identificationVariableDeclarations[0]->rangeVariableDeclaration->abstractSchemaName)
636 28
                ? $AST->fromClause->identificationVariableDeclarations[0]->rangeVariableDeclaration->abstractSchemaName
637 28
                : null;
638
639
            // If the namespace is not given then assumes the first FROM entity namespace
640 28
            if (strpos($className, '\\') === false && ! class_exists($className) && strpos($fromClassName, '\\') !== false) {
641 11
                $namespace  = substr($fromClassName, 0, strrpos($fromClassName, '\\'));
642 11
                $fqcn       = $namespace . '\\' . $className;
643
644 11
                if (class_exists($fqcn)) {
645 11
                    $expression->className  = $fqcn;
646 11
                    $className              = $fqcn;
647
                }
648
            }
649
650 28
            if ( ! class_exists($className)) {
651 1
                $this->semanticalError(sprintf('Class "%s" is not defined.', $className), $token);
652
            }
653
654 27
            $class = new \ReflectionClass($className);
655
656 27
            if ( ! $class->isInstantiable()) {
657 1
                $this->semanticalError(sprintf('Class "%s" can not be instantiated.', $className), $token);
658
            }
659
660 26
            if ($class->getConstructor() === null) {
661 1
                $this->semanticalError(sprintf('Class "%s" has not a valid constructor.', $className), $token);
662
            }
663
664 25
            if ($class->getConstructor()->getNumberOfRequiredParameters() > count($args)) {
665 25
                $this->semanticalError(sprintf('Number of arguments does not match with "%s" constructor declaration.', $className), $token);
666
            }
667
        }
668 24
    }
669
670
    /**
671
     * Validates that the given <tt>PartialObjectExpression</tt> is semantically correct.
672
     * It must exist in query components list.
673
     *
674
     * @return void
675
     */
676 11
    private function processDeferredPartialObjectExpressions()
677
    {
678 11
        foreach ($this->deferredPartialObjectExpressions as $deferredItem) {
679 11
            $expr = $deferredItem['expression'];
680 11
            $class = $this->queryComponents[$expr->identificationVariable]['metadata'];
681
682 11
            foreach ($expr->partialFieldSet as $field) {
683 11
                if (isset($class->fieldMappings[$field])) {
684 10
                    continue;
685
                }
686
687 3
                if (isset($class->associationMappings[$field]) &&
688 3
                    $class->associationMappings[$field]['isOwningSide'] &&
689 3
                    $class->associationMappings[$field]['type'] & ClassMetadata::TO_ONE) {
690 2
                    continue;
691
                }
692
693 1
                $this->semanticalError(
694 1
                    "There is no mapped field named '$field' on class " . $class->name . ".", $deferredItem['token']
695
                );
696
            }
697
698 10
            if (array_intersect($class->identifier, $expr->partialFieldSet) != $class->identifier) {
699 1
                $this->semanticalError(
700 1
                    "The partial field selection of class " . $class->name . " must contain the identifier.",
701 10
                    $deferredItem['token']
702
                );
703
            }
704
        }
705 9
    }
706
707
    /**
708
     * Validates that the given <tt>ResultVariable</tt> is semantically correct.
709
     * It must exist in query components list.
710
     *
711
     * @return void
712
     */
713 30
    private function processDeferredResultVariables()
714
    {
715 30
        foreach ($this->deferredResultVariables as $deferredItem) {
716 30
            $resultVariable = $deferredItem['expression'];
717
718
            // Check if ResultVariable exists in queryComponents
719 30
            if ( ! isset($this->queryComponents[$resultVariable])) {
720
                $this->semanticalError(
721
                    "'$resultVariable' is not defined.", $deferredItem['token']
722
                );
723
            }
724
725 30
            $qComp = $this->queryComponents[$resultVariable];
726
727
            // Check if queryComponent points to an AbstractSchemaName or a ResultVariable
728 30
            if ( ! isset($qComp['resultVariable'])) {
729
                $this->semanticalError(
730
                    "'$resultVariable' does not point to a ResultVariable.", $deferredItem['token']
731
                );
732
            }
733
734
            // Validate if identification variable nesting level is lower or equal than the current one
735 30
            if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) {
736
                $this->semanticalError(
737 30
                    "'$resultVariable' is used outside the scope of its declaration.", $deferredItem['token']
738
                );
739
            }
740
        }
741 30
    }
742
743
    /**
744
     * Validates that the given <tt>PathExpression</tt> is semantically correct for grammar rules:
745
     *
746
     * AssociationPathExpression             ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression
747
     * SingleValuedPathExpression            ::= StateFieldPathExpression | SingleValuedAssociationPathExpression
748
     * StateFieldPathExpression              ::= IdentificationVariable "." StateField
749
     * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField
750
     * CollectionValuedPathExpression        ::= IdentificationVariable "." CollectionValuedAssociationField
751
     *
752
     * @return void
753
     */
754 584
    private function processDeferredPathExpressions()
755
    {
756 584
        foreach ($this->deferredPathExpressions as $deferredItem) {
757 584
            $pathExpression = $deferredItem['expression'];
758
759 584
            $qComp = $this->queryComponents[$pathExpression->identificationVariable];
760 584
            $class = $qComp['metadata'];
761
762 584
            if (($field = $pathExpression->field) === null) {
763 39
                $field = $pathExpression->field = $class->identifier[0];
764
            }
765
766
            // Check if field or association exists
767 584
            if ( ! isset($class->associationMappings[$field]) && ! isset($class->fieldMappings[$field])) {
768 1
                $this->semanticalError(
769 1
                    'Class ' . $class->name . ' has no field or association named ' . $field,
770 1
                    $deferredItem['token']
771
                );
772
            }
773
774 583
            $fieldType = AST\PathExpression::TYPE_STATE_FIELD;
775
776 583
            if (isset($class->associationMappings[$field])) {
777 86
                $assoc = $class->associationMappings[$field];
778
779 86
                $fieldType = ($assoc['type'] & ClassMetadata::TO_ONE)
780 65
                    ? AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION
781 86
                    : AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION;
782
            }
783
784
            // Validate if PathExpression is one of the expected types
785 583
            $expectedType = $pathExpression->expectedType;
786
787 583
            if ( ! ($expectedType & $fieldType)) {
788
                // We need to recognize which was expected type(s)
789 2
                $expectedStringTypes = [];
790
791
                // Validate state field type
792 2
                if ($expectedType & AST\PathExpression::TYPE_STATE_FIELD) {
793 1
                    $expectedStringTypes[] = 'StateFieldPathExpression';
794
                }
795
796
                // Validate single valued association (*-to-one)
797 2
                if ($expectedType & AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION) {
798 2
                    $expectedStringTypes[] = 'SingleValuedAssociationField';
799
                }
800
801
                // Validate single valued association (*-to-many)
802 2
                if ($expectedType & AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION) {
803
                    $expectedStringTypes[] = 'CollectionValuedAssociationField';
804
                }
805
806
                // Build the error message
807 2
                $semanticalError  = 'Invalid PathExpression. ';
808 2
                $semanticalError .= (count($expectedStringTypes) == 1)
809 1
                    ? 'Must be a ' . $expectedStringTypes[0] . '.'
810 2
                    : implode(' or ', $expectedStringTypes) . ' expected.';
811
812 2
                $this->semanticalError($semanticalError, $deferredItem['token']);
813
            }
814
815
            // We need to force the type in PathExpression
816 581
            $pathExpression->type = $fieldType;
817
        }
818 581
    }
819
820
    /**
821
     * @return void
822
     */
823 770
    private function processRootEntityAliasSelected()
824
    {
825 770
        if ( ! count($this->identVariableExpressions)) {
826 222
            return;
827
        }
828
829 559
        foreach ($this->identVariableExpressions as $dqlAlias => $expr) {
830 559
            if (isset($this->queryComponents[$dqlAlias]) && $this->queryComponents[$dqlAlias]['parent'] === null) {
831 559
                return;
832
            }
833
        }
834
835 1
        $this->semanticalError('Cannot select entity through identification variables without choosing at least one root entity alias.');
836
    }
837
838
    /**
839
     * QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement
840
     *
841
     * @return \Doctrine\ORM\Query\AST\SelectStatement |
842
     *         \Doctrine\ORM\Query\AST\UpdateStatement |
843
     *         \Doctrine\ORM\Query\AST\DeleteStatement
844
     */
845 820
    public function QueryLanguage()
846
    {
847 820
        $this->lexer->moveNext();
848
849 820
        switch ($this->lexer->lookahead['type']) {
850 820
            case Lexer::T_SELECT:
851 756
                $statement = $this->SelectStatement();
852 721
                break;
853
854 71
            case Lexer::T_UPDATE:
855 31
                $statement = $this->UpdateStatement();
856 31
                break;
857
858 42
            case Lexer::T_DELETE:
859 41
                $statement = $this->DeleteStatement();
860 40
                break;
861
862
            default:
863 2
                $this->syntaxError('SELECT, UPDATE or DELETE');
864
                break;
865
        }
866
867
        // Check for end of string
868 784
        if ($this->lexer->lookahead !== null) {
869 3
            $this->syntaxError('end of string');
870
        }
871
872 781
        return $statement;
873
    }
874
875
    /**
876
     * SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
877
     *
878
     * @return \Doctrine\ORM\Query\AST\SelectStatement
879
     */
880 756
    public function SelectStatement()
881
    {
882 756
        $selectStatement = new AST\SelectStatement($this->SelectClause(), $this->FromClause());
883
884 725
        $selectStatement->whereClause   = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
885 722
        $selectStatement->groupByClause = $this->lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null;
886 721
        $selectStatement->havingClause  = $this->lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null;
887 721
        $selectStatement->orderByClause = $this->lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null;
888
889 721
        return $selectStatement;
890
    }
891
892
    /**
893
     * UpdateStatement ::= UpdateClause [WhereClause]
894
     *
895
     * @return \Doctrine\ORM\Query\AST\UpdateStatement
896
     */
897 31
    public function UpdateStatement()
898
    {
899 31
        $updateStatement = new AST\UpdateStatement($this->UpdateClause());
900
901 31
        $updateStatement->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
902
903 31
        return $updateStatement;
904
    }
905
906
    /**
907
     * DeleteStatement ::= DeleteClause [WhereClause]
908
     *
909
     * @return \Doctrine\ORM\Query\AST\DeleteStatement
910
     */
911 41
    public function DeleteStatement()
912
    {
913 41
        $deleteStatement = new AST\DeleteStatement($this->DeleteClause());
914
915 40
        $deleteStatement->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
916
917 40
        return $deleteStatement;
918
    }
919
920
    /**
921
     * IdentificationVariable ::= identifier
922
     *
923
     * @return string
924
     */
925 797
    public function IdentificationVariable()
926
    {
927 797
        $this->match(Lexer::T_IDENTIFIER);
928
929 797
        $identVariable = $this->lexer->token['value'];
930
931 797
        $this->deferredIdentificationVariables[] = [
932 797
            'expression'   => $identVariable,
933 797
            'nestingLevel' => $this->nestingLevel,
934 797
            'token'        => $this->lexer->token,
935
        ];
936
937 797
        return $identVariable;
938
    }
939
940
    /**
941
     * AliasIdentificationVariable = identifier
942
     *
943
     * @return string
944
     */
945 791
    public function AliasIdentificationVariable()
946
    {
947 791
        $this->match(Lexer::T_IDENTIFIER);
948
949 791
        $aliasIdentVariable = $this->lexer->token['value'];
950 791
        $exists = isset($this->queryComponents[$aliasIdentVariable]);
951
952 791
        if ($exists) {
953 2
            $this->semanticalError("'$aliasIdentVariable' is already defined.", $this->lexer->token);
954
        }
955
956 791
        return $aliasIdentVariable;
957
    }
958
959
    /**
960
     * AbstractSchemaName ::= fully_qualified_name | aliased_name | identifier
961
     *
962
     * @return string
963
     */
964 811
    public function AbstractSchemaName()
965
    {
966 811
        if ($this->lexer->isNextToken(Lexer::T_FULLY_QUALIFIED_NAME)) {
967 794
            $this->match(Lexer::T_FULLY_QUALIFIED_NAME);
968
969 794
            $schemaName = $this->lexer->token['value'];
970 28
        } else if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER)) {
971 19
            $this->match(Lexer::T_IDENTIFIER);
972
973 19
            $schemaName = $this->lexer->token['value'];
974
        } else {
975 10
            $this->match(Lexer::T_ALIASED_NAME);
976
977 10
            list($namespaceAlias, $simpleClassName) = explode(':', $this->lexer->token['value']);
978
979 10
            $schemaName = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
980
        }
981
982 811
        return $schemaName;
983
    }
984
985
    /**
986
     * Validates an AbstractSchemaName, making sure the class exists.
987
     *
988
     * @param string $schemaName The name to validate.
989
     *
990
     * @throws QueryException if the name does not exist.
991
     */
992 806
    private function validateAbstractSchemaName($schemaName)
993
    {
994 806
        if (! (class_exists($schemaName, true) || interface_exists($schemaName, true))) {
995 16
            $this->semanticalError("Class '$schemaName' is not defined.", $this->lexer->token);
996
        }
997 791
    }
998
999
    /**
1000
     * AliasResultVariable ::= identifier
1001
     *
1002
     * @return string
1003
     */
1004 119
    public function AliasResultVariable()
1005
    {
1006 119
        $this->match(Lexer::T_IDENTIFIER);
1007
1008 115
        $resultVariable = $this->lexer->token['value'];
1009 115
        $exists = isset($this->queryComponents[$resultVariable]);
1010
1011 115
        if ($exists) {
1012 2
            $this->semanticalError("'$resultVariable' is already defined.", $this->lexer->token);
1013
        }
1014
1015 115
        return $resultVariable;
1016
    }
1017
1018
    /**
1019
     * ResultVariable ::= identifier
1020
     *
1021
     * @return string
1022
     */
1023 30
    public function ResultVariable()
1024
    {
1025 30
        $this->match(Lexer::T_IDENTIFIER);
1026
1027 30
        $resultVariable = $this->lexer->token['value'];
1028
1029
        // Defer ResultVariable validation
1030 30
        $this->deferredResultVariables[] = [
1031 30
            'expression'   => $resultVariable,
1032 30
            'nestingLevel' => $this->nestingLevel,
1033 30
            'token'        => $this->lexer->token,
1034
        ];
1035
1036 30
        return $resultVariable;
1037
    }
1038
1039
    /**
1040
     * JoinAssociationPathExpression ::= IdentificationVariable "." (CollectionValuedAssociationField | SingleValuedAssociationField)
1041
     *
1042
     * @return \Doctrine\ORM\Query\AST\JoinAssociationPathExpression
1043
     */
1044 254
    public function JoinAssociationPathExpression()
1045
    {
1046 254
        $identVariable = $this->IdentificationVariable();
1047
1048 254
        if ( ! isset($this->queryComponents[$identVariable])) {
1049
            $this->semanticalError(
1050
                'Identification Variable ' . $identVariable .' used in join path expression but was not defined before.'
1051
            );
1052
        }
1053
1054 254
        $this->match(Lexer::T_DOT);
1055 254
        $this->match(Lexer::T_IDENTIFIER);
1056
1057 254
        $field = $this->lexer->token['value'];
1058
1059
        // Validate association field
1060 254
        $qComp = $this->queryComponents[$identVariable];
1061 254
        $class = $qComp['metadata'];
1062
1063 254
        if ( ! $class->hasAssociation($field)) {
1064
            $this->semanticalError('Class ' . $class->name . ' has no association named ' . $field);
1065
        }
1066
1067 254
        return new AST\JoinAssociationPathExpression($identVariable, $field);
1068
    }
1069
1070
    /**
1071
     * Parses an arbitrary path expression and defers semantical validation
1072
     * based on expected types.
1073
     *
1074
     * PathExpression ::= IdentificationVariable {"." identifier}*
1075
     *
1076
     * @param integer $expectedTypes
1077
     *
1078
     * @return \Doctrine\ORM\Query\AST\PathExpression
1079
     */
1080 594
    public function PathExpression($expectedTypes)
1081
    {
1082 594
        $identVariable = $this->IdentificationVariable();
1083 594
        $field = null;
1084
1085 594
        if ($this->lexer->isNextToken(Lexer::T_DOT)) {
1086 587
            $this->match(Lexer::T_DOT);
1087 587
            $this->match(Lexer::T_IDENTIFIER);
1088
1089 587
            $field = $this->lexer->token['value'];
1090
1091 587
            while ($this->lexer->isNextToken(Lexer::T_DOT)) {
1092 2
                $this->match(Lexer::T_DOT);
1093 2
                $this->match(Lexer::T_IDENTIFIER);
1094 2
                $field .= '.'.$this->lexer->token['value'];
1095
            }
1096
        }
1097
1098
        // Creating AST node
1099 594
        $pathExpr = new AST\PathExpression($expectedTypes, $identVariable, $field);
1100
1101
        // Defer PathExpression validation if requested to be deferred
1102 594
        $this->deferredPathExpressions[] = [
1103 594
            'expression'   => $pathExpr,
1104 594
            'nestingLevel' => $this->nestingLevel,
1105 594
            'token'        => $this->lexer->token,
1106
        ];
1107
1108 594
        return $pathExpr;
1109
    }
1110
1111
    /**
1112
     * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression
1113
     *
1114
     * @return \Doctrine\ORM\Query\AST\PathExpression
1115
     */
1116
    public function AssociationPathExpression()
1117
    {
1118
        return $this->PathExpression(
1119
            AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION |
1120
            AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION
1121
        );
1122
    }
1123
1124
    /**
1125
     * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression
1126
     *
1127
     * @return \Doctrine\ORM\Query\AST\PathExpression
1128
     */
1129 503
    public function SingleValuedPathExpression()
1130
    {
1131 503
        return $this->PathExpression(
1132 503
            AST\PathExpression::TYPE_STATE_FIELD |
1133 503
            AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION
1134
        );
1135
    }
1136
1137
    /**
1138
     * StateFieldPathExpression ::= IdentificationVariable "." StateField
1139
     *
1140
     * @return \Doctrine\ORM\Query\AST\PathExpression
1141
     */
1142 202
    public function StateFieldPathExpression()
1143
    {
1144 202
        return $this->PathExpression(AST\PathExpression::TYPE_STATE_FIELD);
1145
    }
1146
1147
    /**
1148
     * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField
1149
     *
1150
     * @return \Doctrine\ORM\Query\AST\PathExpression
1151
     */
1152 9
    public function SingleValuedAssociationPathExpression()
1153
    {
1154 9
        return $this->PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION);
1155
    }
1156
1157
    /**
1158
     * CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField
1159
     *
1160
     * @return \Doctrine\ORM\Query\AST\PathExpression
1161
     */
1162 21
    public function CollectionValuedPathExpression()
1163
    {
1164 21
        return $this->PathExpression(AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION);
1165
    }
1166
1167
    /**
1168
     * SelectClause ::= "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression}
1169
     *
1170
     * @return \Doctrine\ORM\Query\AST\SelectClause
1171
     */
1172 756
    public function SelectClause()
1173
    {
1174 756
        $isDistinct = false;
1175 756
        $this->match(Lexer::T_SELECT);
1176
1177
        // Check for DISTINCT
1178 756
        if ($this->lexer->isNextToken(Lexer::T_DISTINCT)) {
1179 5
            $this->match(Lexer::T_DISTINCT);
1180
1181 5
            $isDistinct = true;
1182
        }
1183
1184
        // Process SelectExpressions (1..N)
1185 756
        $selectExpressions = [];
1186 756
        $selectExpressions[] = $this->SelectExpression();
1187
1188 748
        while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
1189 282
            $this->match(Lexer::T_COMMA);
1190
1191 282
            $selectExpressions[] = $this->SelectExpression();
1192
        }
1193
1194 747
        return new AST\SelectClause($selectExpressions, $isDistinct);
1195
    }
1196
1197
    /**
1198
     * SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression
1199
     *
1200
     * @return \Doctrine\ORM\Query\AST\SimpleSelectClause
1201
     */
1202 48
    public function SimpleSelectClause()
1203
    {
1204 48
        $isDistinct = false;
1205 48
        $this->match(Lexer::T_SELECT);
1206
1207 48
        if ($this->lexer->isNextToken(Lexer::T_DISTINCT)) {
1208
            $this->match(Lexer::T_DISTINCT);
1209
1210
            $isDistinct = true;
1211
        }
1212
1213 48
        return new AST\SimpleSelectClause($this->SimpleSelectExpression(), $isDistinct);
1214
    }
1215
1216
    /**
1217
     * UpdateClause ::= "UPDATE" AbstractSchemaName ["AS"] AliasIdentificationVariable "SET" UpdateItem {"," UpdateItem}*
1218
     *
1219
     * @return \Doctrine\ORM\Query\AST\UpdateClause
1220
     */
1221 31
    public function UpdateClause()
1222
    {
1223 31
        $this->match(Lexer::T_UPDATE);
1224
1225 31
        $token = $this->lexer->lookahead;
1226 31
        $abstractSchemaName = $this->AbstractSchemaName();
1227
1228 31
        $this->validateAbstractSchemaName($abstractSchemaName);
1229
1230 31
        if ($this->lexer->isNextToken(Lexer::T_AS)) {
1231 1
            $this->match(Lexer::T_AS);
1232
        }
1233
1234 31
        $aliasIdentificationVariable = $this->AliasIdentificationVariable();
1235
1236 31
        $class = $this->em->getClassMetadata($abstractSchemaName);
1237
1238
        // Building queryComponent
1239
        $queryComponent = [
1240 31
            'metadata'     => $class,
1241
            'parent'       => null,
1242
            'relation'     => null,
1243
            'map'          => null,
1244 31
            'nestingLevel' => $this->nestingLevel,
1245 31
            'token'        => $token,
1246
        ];
1247
1248 31
        $this->queryComponents[$aliasIdentificationVariable] = $queryComponent;
1249
1250 31
        $this->match(Lexer::T_SET);
1251
1252 31
        $updateItems = [];
1253 31
        $updateItems[] = $this->UpdateItem();
1254
1255 31
        while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
1256 5
            $this->match(Lexer::T_COMMA);
1257
1258 5
            $updateItems[] = $this->UpdateItem();
1259
        }
1260
1261 31
        $updateClause = new AST\UpdateClause($abstractSchemaName, $updateItems);
1262 31
        $updateClause->aliasIdentificationVariable = $aliasIdentificationVariable;
1263
1264 31
        return $updateClause;
1265
    }
1266
1267
    /**
1268
     * DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName ["AS"] AliasIdentificationVariable
1269
     *
1270
     * @return \Doctrine\ORM\Query\AST\DeleteClause
1271
     */
1272 41
    public function DeleteClause()
1273
    {
1274 41
        $this->match(Lexer::T_DELETE);
1275
1276 41
        if ($this->lexer->isNextToken(Lexer::T_FROM)) {
1277 8
            $this->match(Lexer::T_FROM);
1278
        }
1279
1280 41
        $token = $this->lexer->lookahead;
1281 41
        $abstractSchemaName = $this->AbstractSchemaName();
1282
1283 41
        $this->validateAbstractSchemaName($abstractSchemaName);
1284
1285 41
        $deleteClause = new AST\DeleteClause($abstractSchemaName);
1286
1287 41
        if ($this->lexer->isNextToken(Lexer::T_AS)) {
1288 1
            $this->match(Lexer::T_AS);
1289
        }
1290
1291 41
        $aliasIdentificationVariable = $this->AliasIdentificationVariable();
1292
1293 40
        $deleteClause->aliasIdentificationVariable = $aliasIdentificationVariable;
1294 40
        $class = $this->em->getClassMetadata($deleteClause->abstractSchemaName);
1295
1296
        // Building queryComponent
1297
        $queryComponent = [
1298 40
            'metadata'     => $class,
1299
            'parent'       => null,
1300
            'relation'     => null,
1301
            'map'          => null,
1302 40
            'nestingLevel' => $this->nestingLevel,
1303 40
            'token'        => $token,
1304
        ];
1305
1306 40
        $this->queryComponents[$aliasIdentificationVariable] = $queryComponent;
1307
1308 40
        return $deleteClause;
1309
    }
1310
1311
    /**
1312
     * FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}*
1313
     *
1314
     * @return \Doctrine\ORM\Query\AST\FromClause
1315
     */
1316 747
    public function FromClause()
1317
    {
1318 747
        $this->match(Lexer::T_FROM);
1319
1320 742
        $identificationVariableDeclarations = [];
1321 742
        $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration();
1322
1323 725
        while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
1324 6
            $this->match(Lexer::T_COMMA);
1325
1326 6
            $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration();
1327
        }
1328
1329 725
        return new AST\FromClause($identificationVariableDeclarations);
1330
    }
1331
1332
    /**
1333
     * SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}*
1334
     *
1335
     * @return \Doctrine\ORM\Query\AST\SubselectFromClause
1336
     */
1337 48
    public function SubselectFromClause()
1338
    {
1339 48
        $this->match(Lexer::T_FROM);
1340
1341 48
        $identificationVariables = [];
1342 48
        $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration();
1343
1344 47
        while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
1345
            $this->match(Lexer::T_COMMA);
1346
1347
            $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration();
1348
        }
1349
1350 47
        return new AST\SubselectFromClause($identificationVariables);
1351
    }
1352
1353
    /**
1354
     * WhereClause ::= "WHERE" ConditionalExpression
1355
     *
1356
     * @return \Doctrine\ORM\Query\AST\WhereClause
1357
     */
1358 330
    public function WhereClause()
1359
    {
1360 330
        $this->match(Lexer::T_WHERE);
1361
1362 330
        return new AST\WhereClause($this->ConditionalExpression());
1363
    }
1364
1365
    /**
1366
     * HavingClause ::= "HAVING" ConditionalExpression
1367
     *
1368
     * @return \Doctrine\ORM\Query\AST\HavingClause
1369
     */
1370 21
    public function HavingClause()
1371
    {
1372 21
        $this->match(Lexer::T_HAVING);
1373
1374 21
        return new AST\HavingClause($this->ConditionalExpression());
1375
    }
1376
1377
    /**
1378
     * GroupByClause ::= "GROUP" "BY" GroupByItem {"," GroupByItem}*
1379
     *
1380
     * @return \Doctrine\ORM\Query\AST\GroupByClause
1381
     */
1382 31
    public function GroupByClause()
1383
    {
1384 31
        $this->match(Lexer::T_GROUP);
1385 31
        $this->match(Lexer::T_BY);
1386
1387 31
        $groupByItems = [$this->GroupByItem()];
1388
1389 30
        while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
1390 8
            $this->match(Lexer::T_COMMA);
1391
1392 8
            $groupByItems[] = $this->GroupByItem();
1393
        }
1394
1395 30
        return new AST\GroupByClause($groupByItems);
1396
    }
1397
1398
    /**
1399
     * OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}*
1400
     *
1401
     * @return \Doctrine\ORM\Query\AST\OrderByClause
1402
     */
1403 179
    public function OrderByClause()
1404
    {
1405 179
        $this->match(Lexer::T_ORDER);
1406 179
        $this->match(Lexer::T_BY);
1407
1408 179
        $orderByItems = [];
1409 179
        $orderByItems[] = $this->OrderByItem();
1410
1411 179
        while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
1412 14
            $this->match(Lexer::T_COMMA);
1413
1414 14
            $orderByItems[] = $this->OrderByItem();
1415
        }
1416
1417 179
        return new AST\OrderByClause($orderByItems);
1418
    }
1419
1420
    /**
1421
     * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
1422
     *
1423
     * @return \Doctrine\ORM\Query\AST\Subselect
1424
     */
1425 48
    public function Subselect()
1426
    {
1427
        // Increase query nesting level
1428 48
        $this->nestingLevel++;
1429
1430 48
        $subselect = new AST\Subselect($this->SimpleSelectClause(), $this->SubselectFromClause());
1431
1432 47
        $subselect->whereClause   = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null;
1433 47
        $subselect->groupByClause = $this->lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null;
1434 47
        $subselect->havingClause  = $this->lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null;
1435 47
        $subselect->orderByClause = $this->lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null;
1436
1437
        // Decrease query nesting level
1438 47
        $this->nestingLevel--;
1439
1440 47
        return $subselect;
1441
    }
1442
1443
    /**
1444
     * UpdateItem ::= SingleValuedPathExpression "=" NewValue
1445
     *
1446
     * @return \Doctrine\ORM\Query\AST\UpdateItem
1447
     */
1448 31
    public function UpdateItem()
1449
    {
1450 31
        $pathExpr = $this->SingleValuedPathExpression();
1451
1452 31
        $this->match(Lexer::T_EQUALS);
1453
1454 31
        $updateItem = new AST\UpdateItem($pathExpr, $this->NewValue());
1455
1456 31
        return $updateItem;
1457
    }
1458
1459
    /**
1460
     * GroupByItem ::= IdentificationVariable | ResultVariable | SingleValuedPathExpression
1461
     *
1462
     * @return string | \Doctrine\ORM\Query\AST\PathExpression
1463
     */
1464 31
    public function GroupByItem()
1465
    {
1466
        // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression
1467 31
        $glimpse = $this->lexer->glimpse();
1468
1469 31
        if ($glimpse['type'] === Lexer::T_DOT) {
1470 12
            return $this->SingleValuedPathExpression();
1471
        }
1472
1473
        // Still need to decide between IdentificationVariable or ResultVariable
1474 19
        $lookaheadValue = $this->lexer->lookahead['value'];
1475
1476 19
        if ( ! isset($this->queryComponents[$lookaheadValue])) {
1477 1
            $this->semanticalError('Cannot group by undefined identification or result variable.');
1478
        }
1479
1480 18
        return (isset($this->queryComponents[$lookaheadValue]['metadata']))
1481 16
            ? $this->IdentificationVariable()
1482 18
            : $this->ResultVariable();
1483
    }
1484
1485
    /**
1486
     * OrderByItem ::= (
1487
     *      SimpleArithmeticExpression | SingleValuedPathExpression |
1488
     *      ScalarExpression | ResultVariable | FunctionDeclaration
1489
     * ) ["ASC" | "DESC"]
1490
     *
1491
     * @return \Doctrine\ORM\Query\AST\OrderByItem
1492
     */
1493 179
    public function OrderByItem()
1494
    {
1495 179
        $this->lexer->peek(); // lookahead => '.'
1496 179
        $this->lexer->peek(); // lookahead => token after '.'
1497
1498 179
        $peek = $this->lexer->peek(); // lookahead => token after the token after the '.'
1499
1500 179
        $this->lexer->resetPeek();
1501
1502 179
        $glimpse = $this->lexer->glimpse();
1503
1504
        switch (true) {
1505 179
            case ($this->isFunction()):
1506 1
                $expr = $this->FunctionDeclaration();
1507 1
                break;
1508
1509 178
            case ($this->isMathOperator($peek)):
1510 25
                $expr = $this->SimpleArithmeticExpression();
1511 25
                break;
1512
1513 154
            case ($glimpse['type'] === Lexer::T_DOT):
1514 141
                $expr = $this->SingleValuedPathExpression();
1515 141
                break;
1516
1517 17
            case ($this->lexer->peek() && $this->isMathOperator($this->peekBeyondClosingParenthesis())):
1518 2
                $expr = $this->ScalarExpression();
1519 2
                break;
1520
1521
            default:
1522 15
                $expr = $this->ResultVariable();
1523 15
                break;
1524
        }
1525
1526 179
        $type = 'ASC';
1527 179
        $item = new AST\OrderByItem($expr);
1528
1529
        switch (true) {
1530 179
            case ($this->lexer->isNextToken(Lexer::T_DESC)):
1531 93
                $this->match(Lexer::T_DESC);
1532 93
                $type = 'DESC';
1533 93
                break;
1534
1535 152
            case ($this->lexer->isNextToken(Lexer::T_ASC)):
1536 96
                $this->match(Lexer::T_ASC);
1537 96
                break;
1538
1539
            default:
1540
                // Do nothing
1541
        }
1542
1543 179
        $item->type = $type;
1544
1545 179
        return $item;
1546
    }
1547
1548
    /**
1549
     * NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary |
1550
     *      EnumPrimary | SimpleEntityExpression | "NULL"
1551
     *
1552
     * NOTE: Since it is not possible to correctly recognize individual types, here is the full
1553
     * grammar that needs to be supported:
1554
     *
1555
     * NewValue ::= SimpleArithmeticExpression | "NULL"
1556
     *
1557
     * SimpleArithmeticExpression covers all *Primary grammar rules and also SimpleEntityExpression
1558
     *
1559
     * @return AST\ArithmeticExpression
1560
     */
1561 31
    public function NewValue()
1562
    {
1563 31
        if ($this->lexer->isNextToken(Lexer::T_NULL)) {
1564 1
            $this->match(Lexer::T_NULL);
1565
1566 1
            return null;
1567
        }
1568
1569 30
        if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
1570 18
            $this->match(Lexer::T_INPUT_PARAMETER);
1571
1572 18
            return new AST\InputParameter($this->lexer->token['value']);
1573
        }
1574
1575 12
        return $this->ArithmeticExpression();
1576
    }
1577
1578
    /**
1579
     * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {Join}*
1580
     *
1581
     * @return \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration
1582
     */
1583 744
    public function IdentificationVariableDeclaration()
1584
    {
1585 744
        $joins                    = [];
1586 744
        $rangeVariableDeclaration = $this->RangeVariableDeclaration();
1587 729
        $indexBy                  = $this->lexer->isNextToken(Lexer::T_INDEX)
1588 7
            ? $this->IndexBy()
1589 729
            : null;
1590
1591 729
        $rangeVariableDeclaration->isRoot = true;
1592
1593
        while (
1594 729
            $this->lexer->isNextToken(Lexer::T_LEFT) ||
1595 729
            $this->lexer->isNextToken(Lexer::T_INNER) ||
1596 729
            $this->lexer->isNextToken(Lexer::T_JOIN)
1597
        ) {
1598 275
            $joins[] = $this->Join();
1599
        }
1600
1601 727
        return new AST\IdentificationVariableDeclaration(
1602 727
            $rangeVariableDeclaration, $indexBy, $joins
1603
        );
1604
    }
1605
1606
    /**
1607
     * SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration
1608
     *
1609
     * {Internal note: WARNING: Solution is harder than a bare implementation.
1610
     * Desired EBNF support:
1611
     *
1612
     * SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration | (AssociationPathExpression ["AS"] AliasIdentificationVariable)
1613
     *
1614
     * It demands that entire SQL generation to become programmatical. This is
1615
     * needed because association based subselect requires "WHERE" conditional
1616
     * expressions to be injected, but there is no scope to do that. Only scope
1617
     * accessible is "FROM", prohibiting an easy implementation without larger
1618
     * changes.}
1619
     *
1620
     * @return \Doctrine\ORM\Query\AST\SubselectIdentificationVariableDeclaration |
1621
     *         \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration
1622
     */
1623 48
    public function SubselectIdentificationVariableDeclaration()
1624
    {
1625
        /*
1626
        NOT YET IMPLEMENTED!
1627
1628
        $glimpse = $this->lexer->glimpse();
1629
1630
        if ($glimpse['type'] == Lexer::T_DOT) {
1631
            $associationPathExpression = $this->AssociationPathExpression();
1632
1633
            if ($this->lexer->isNextToken(Lexer::T_AS)) {
1634
                $this->match(Lexer::T_AS);
1635
            }
1636
1637
            $aliasIdentificationVariable = $this->AliasIdentificationVariable();
1638
            $identificationVariable      = $associationPathExpression->identificationVariable;
1639
            $field                       = $associationPathExpression->associationField;
1640
1641
            $class       = $this->queryComponents[$identificationVariable]['metadata'];
1642
            $targetClass = $this->em->getClassMetadata($class->associationMappings[$field]['targetEntity']);
1643
1644
            // Building queryComponent
1645
            $joinQueryComponent = array(
1646
                'metadata'     => $targetClass,
1647
                'parent'       => $identificationVariable,
1648
                'relation'     => $class->getAssociationMapping($field),
1649
                'map'          => null,
1650
                'nestingLevel' => $this->nestingLevel,
1651
                'token'        => $this->lexer->lookahead
1652
            );
1653
1654
            $this->queryComponents[$aliasIdentificationVariable] = $joinQueryComponent;
1655
1656
            return new AST\SubselectIdentificationVariableDeclaration(
1657
                $associationPathExpression, $aliasIdentificationVariable
1658
            );
1659
        }
1660
        */
1661
1662 48
        return $this->IdentificationVariableDeclaration();
1663
    }
1664
1665
    /**
1666
     * Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN"
1667
     *          (JoinAssociationDeclaration | RangeVariableDeclaration)
1668
     *          ["WITH" ConditionalExpression]
1669
     *
1670
     * @return \Doctrine\ORM\Query\AST\Join
1671
     */
1672 275
    public function Join()
1673
    {
1674
        // Check Join type
1675 275
        $joinType = AST\Join::JOIN_TYPE_INNER;
1676
1677
        switch (true) {
1678 275
            case ($this->lexer->isNextToken(Lexer::T_LEFT)):
1679 66
                $this->match(Lexer::T_LEFT);
1680
1681 66
                $joinType = AST\Join::JOIN_TYPE_LEFT;
1682
1683
                // Possible LEFT OUTER join
1684 66
                if ($this->lexer->isNextToken(Lexer::T_OUTER)) {
1685
                    $this->match(Lexer::T_OUTER);
1686
1687
                    $joinType = AST\Join::JOIN_TYPE_LEFTOUTER;
1688
                }
1689 66
                break;
1690
1691 213
            case ($this->lexer->isNextToken(Lexer::T_INNER)):
1692 20
                $this->match(Lexer::T_INNER);
1693 20
                break;
1694
1695
            default:
1696
                // Do nothing
1697
        }
1698
1699 275
        $this->match(Lexer::T_JOIN);
1700
1701 275
        $next            = $this->lexer->glimpse();
1702 275
        $joinDeclaration = ($next['type'] === Lexer::T_DOT) ? $this->JoinAssociationDeclaration() : $this->RangeVariableDeclaration();
1703 273
        $adhocConditions = $this->lexer->isNextToken(Lexer::T_WITH);
1704 273
        $join            = new AST\Join($joinType, $joinDeclaration);
1705
1706
        // Describe non-root join declaration
1707 273
        if ($joinDeclaration instanceof AST\RangeVariableDeclaration) {
1708 21
            $joinDeclaration->isRoot = false;
1709
        }
1710
1711
        // Check for ad-hoc Join conditions
1712 273
        if ($adhocConditions) {
1713 22
            $this->match(Lexer::T_WITH);
1714
1715 22
            $join->conditionalExpression = $this->ConditionalExpression();
1716
        }
1717
1718 273
        return $join;
1719
    }
1720
1721
    /**
1722
     * RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable
1723
     *
1724
     * @return \Doctrine\ORM\Query\AST\RangeVariableDeclaration
1725
     */
1726 744
    public function RangeVariableDeclaration()
1727
    {
1728 744
        $abstractSchemaName = $this->AbstractSchemaName();
1729
1730 744
        $this->validateAbstractSchemaName($abstractSchemaName);
1731
1732 729
        if ($this->lexer->isNextToken(Lexer::T_AS)) {
1733 5
            $this->match(Lexer::T_AS);
1734
        }
1735
1736 729
        $token = $this->lexer->lookahead;
1737 729
        $aliasIdentificationVariable = $this->AliasIdentificationVariable();
1738 729
        $classMetadata = $this->em->getClassMetadata($abstractSchemaName);
1739
1740
        // Building queryComponent
1741
        $queryComponent = [
1742 729
            'metadata'     => $classMetadata,
1743
            'parent'       => null,
1744
            'relation'     => null,
1745
            'map'          => null,
1746 729
            'nestingLevel' => $this->nestingLevel,
1747 729
            'token'        => $token
1748
        ];
1749
1750 729
        $this->queryComponents[$aliasIdentificationVariable] = $queryComponent;
1751
1752 729
        return new AST\RangeVariableDeclaration($abstractSchemaName, $aliasIdentificationVariable);
1753
    }
1754
1755
    /**
1756
     * JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable [IndexBy]
1757
     *
1758
     * @return \Doctrine\ORM\Query\AST\JoinAssociationPathExpression
1759
     */
1760 254
    public function JoinAssociationDeclaration()
1761
    {
1762 254
        $joinAssociationPathExpression = $this->JoinAssociationPathExpression();
1763
1764 254
        if ($this->lexer->isNextToken(Lexer::T_AS)) {
1765 4
            $this->match(Lexer::T_AS);
1766
        }
1767
1768 254
        $aliasIdentificationVariable = $this->AliasIdentificationVariable();
1769 252
        $indexBy                     = $this->lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null;
1770
1771 252
        $identificationVariable = $joinAssociationPathExpression->identificationVariable;
1772 252
        $field                  = $joinAssociationPathExpression->associationField;
1773
1774 252
        $class       = $this->queryComponents[$identificationVariable]['metadata'];
1775 252
        $targetClass = $this->em->getClassMetadata($class->associationMappings[$field]['targetEntity']);
1776
1777
        // Building queryComponent
1778
        $joinQueryComponent = [
1779 252
            'metadata'     => $targetClass,
1780 252
            'parent'       => $joinAssociationPathExpression->identificationVariable,
1781 252
            'relation'     => $class->getAssociationMapping($field),
1782
            'map'          => null,
1783 252
            'nestingLevel' => $this->nestingLevel,
1784 252
            'token'        => $this->lexer->lookahead
1785
        ];
1786
1787 252
        $this->queryComponents[$aliasIdentificationVariable] = $joinQueryComponent;
1788
1789 252
        return new AST\JoinAssociationDeclaration($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy);
1790
    }
1791
1792
    /**
1793
     * PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet
1794
     * PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}"
1795
     *
1796
     * @return \Doctrine\ORM\Query\AST\PartialObjectExpression
1797
     */
1798 11
    public function PartialObjectExpression()
1799
    {
1800 11
        $this->match(Lexer::T_PARTIAL);
1801
1802 11
        $partialFieldSet = [];
1803
1804 11
        $identificationVariable = $this->IdentificationVariable();
1805
1806 11
        $this->match(Lexer::T_DOT);
1807 11
        $this->match(Lexer::T_OPEN_CURLY_BRACE);
1808 11
        $this->match(Lexer::T_IDENTIFIER);
1809
1810 11
        $field = $this->lexer->token['value'];
1811
1812
        // First field in partial expression might be embeddable property
1813 11
        while ($this->lexer->isNextToken(Lexer::T_DOT)) {
1814 1
            $this->match(Lexer::T_DOT);
1815 1
            $this->match(Lexer::T_IDENTIFIER);
1816 1
            $field .= '.'.$this->lexer->token['value'];
1817
        }
1818
1819 11
        $partialFieldSet[] = $field;
1820
1821 11
        while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
1822 9
            $this->match(Lexer::T_COMMA);
1823 9
            $this->match(Lexer::T_IDENTIFIER);
1824
1825 9
            $field = $this->lexer->token['value'];
1826
1827 9
            while ($this->lexer->isNextToken(Lexer::T_DOT)) {
1828 2
                $this->match(Lexer::T_DOT);
1829 2
                $this->match(Lexer::T_IDENTIFIER);
1830 2
                $field .= '.'.$this->lexer->token['value'];
1831
            }
1832
1833 9
            $partialFieldSet[] = $field;
1834
        }
1835
1836 11
        $this->match(Lexer::T_CLOSE_CURLY_BRACE);
1837
1838 11
        $partialObjectExpression = new AST\PartialObjectExpression($identificationVariable, $partialFieldSet);
1839
1840
        // Defer PartialObjectExpression validation
1841 11
        $this->deferredPartialObjectExpressions[] = [
1842 11
            'expression'   => $partialObjectExpression,
1843 11
            'nestingLevel' => $this->nestingLevel,
1844 11
            'token'        => $this->lexer->token,
1845
        ];
1846
1847 11
        return $partialObjectExpression;
1848
    }
1849
1850
    /**
1851
     * NewObjectExpression ::= "NEW" AbstractSchemaName "(" NewObjectArg {"," NewObjectArg}* ")"
1852
     *
1853
     * @return \Doctrine\ORM\Query\AST\NewObjectExpression
1854
     */
1855 28
    public function NewObjectExpression()
1856
    {
1857 28
        $this->match(Lexer::T_NEW);
1858
1859 28
        $className = $this->AbstractSchemaName(); // note that this is not yet validated
1860 28
        $token = $this->lexer->token;
1861
1862 28
        $this->match(Lexer::T_OPEN_PARENTHESIS);
1863
1864 28
        $args[] = $this->NewObjectArg();
1865
1866 28
        while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
1867 24
            $this->match(Lexer::T_COMMA);
1868
1869 24
            $args[] = $this->NewObjectArg();
1870
        }
1871
1872 28
        $this->match(Lexer::T_CLOSE_PARENTHESIS);
1873
1874 28
        $expression = new AST\NewObjectExpression($className, $args);
1875
1876
        // Defer NewObjectExpression validation
1877 28
        $this->deferredNewObjectExpressions[] = [
1878 28
            'token'        => $token,
1879 28
            'expression'   => $expression,
1880 28
            'nestingLevel' => $this->nestingLevel,
1881
        ];
1882
1883 28
        return $expression;
1884
    }
1885
1886
    /**
1887
     * NewObjectArg ::= ScalarExpression | "(" Subselect ")"
1888
     *
1889
     * @return mixed
1890
     */
1891 28
    public function NewObjectArg()
1892
    {
1893 28
        $token = $this->lexer->lookahead;
1894 28
        $peek  = $this->lexer->glimpse();
1895
1896 28
        if ($token['type'] === Lexer::T_OPEN_PARENTHESIS && $peek['type'] === Lexer::T_SELECT) {
1897 2
            $this->match(Lexer::T_OPEN_PARENTHESIS);
1898 2
            $expression = $this->Subselect();
1899 2
            $this->match(Lexer::T_CLOSE_PARENTHESIS);
1900
1901 2
            return $expression;
1902
        }
1903
1904 28
        return $this->ScalarExpression();
1905
    }
1906
1907
    /**
1908
     * IndexBy ::= "INDEX" "BY" StateFieldPathExpression
1909
     *
1910
     * @return \Doctrine\ORM\Query\AST\IndexBy
1911
     */
1912 11
    public function IndexBy()
1913
    {
1914 11
        $this->match(Lexer::T_INDEX);
1915 11
        $this->match(Lexer::T_BY);
1916 11
        $pathExpr = $this->StateFieldPathExpression();
1917
1918
        // Add the INDEX BY info to the query component
1919 11
        $this->queryComponents[$pathExpr->identificationVariable]['map'] = $pathExpr->field;
1920
1921 11
        return new AST\IndexBy($pathExpr);
1922
    }
1923
1924
    /**
1925
     * ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary |
1926
     *                      StateFieldPathExpression | BooleanPrimary | CaseExpression |
1927
     *                      InstanceOfExpression
1928
     *
1929
     * @return mixed One of the possible expressions or subexpressions.
1930
     */
1931 160
    public function ScalarExpression()
1932
    {
1933 160
        $lookahead = $this->lexer->lookahead['type'];
1934 160
        $peek      = $this->lexer->glimpse();
1935
1936
        switch (true) {
1937 160
            case ($lookahead === Lexer::T_INTEGER):
1938 157
            case ($lookahead === Lexer::T_FLOAT):
1939
            // SimpleArithmeticExpression : (- u.value ) or ( + u.value )  or ( - 1 ) or ( + 1 )
1940 157
            case ($lookahead === Lexer::T_MINUS):
1941 157
            case ($lookahead === Lexer::T_PLUS):
1942 17
                return $this->SimpleArithmeticExpression();
1943
1944 157
            case ($lookahead === Lexer::T_STRING):
1945 13
                return $this->StringPrimary();
1946
1947 155
            case ($lookahead === Lexer::T_TRUE):
1948 155
            case ($lookahead === Lexer::T_FALSE):
1949 3
                $this->match($lookahead);
1950
1951 3
                return new AST\Literal(AST\Literal::BOOLEAN, $this->lexer->token['value']);
1952
1953 155
            case ($lookahead === Lexer::T_INPUT_PARAMETER):
1954
                switch (true) {
1955 1
                    case $this->isMathOperator($peek):
1956
                        // :param + u.value
1957 1
                        return $this->SimpleArithmeticExpression();
1958
                    default:
1959
                        return $this->InputParameter();
1960
                }
1961
1962 155
            case ($lookahead === Lexer::T_CASE):
1963 151
            case ($lookahead === Lexer::T_COALESCE):
1964 151
            case ($lookahead === Lexer::T_NULLIF):
1965
                // Since NULLIF and COALESCE can be identified as a function,
1966
                // we need to check these before checking for FunctionDeclaration
1967 8
                return $this->CaseExpression();
1968
1969 151
            case ($lookahead === Lexer::T_OPEN_PARENTHESIS):
1970 4
                return $this->SimpleArithmeticExpression();
1971
1972
            // this check must be done before checking for a filed path expression
1973 148
            case ($this->isFunction()):
1974 26
                $this->lexer->peek(); // "("
1975
1976
                switch (true) {
1977 26
                    case ($this->isMathOperator($this->peekBeyondClosingParenthesis())):
1978
                        // SUM(u.id) + COUNT(u.id)
1979 7
                        return $this->SimpleArithmeticExpression();
1980
1981 21
                    case ($this->isAggregateFunction($this->lexer->lookahead['type'])):
1982 19
                        return $this->AggregateExpression();
1983
1984
                    default:
1985
                        // IDENTITY(u)
1986 2
                        return $this->FunctionDeclaration();
1987
                }
1988
1989
                break;
0 ignored issues
show
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1990
            // it is no function, so it must be a field path
1991 130
            case ($lookahead === Lexer::T_IDENTIFIER):
1992 130
                $this->lexer->peek(); // lookahead => '.'
1993 130
                $this->lexer->peek(); // lookahead => token after '.'
1994 130
                $peek = $this->lexer->peek(); // lookahead => token after the token after the '.'
1995 130
                $this->lexer->resetPeek();
1996
1997 130
                if ($this->isMathOperator($peek)) {
1998 7
                    return $this->SimpleArithmeticExpression();
1999
                }
2000
2001 125
                return $this->StateFieldPathExpression();
2002
2003
            default:
2004
                $this->syntaxError();
2005
        }
2006
    }
2007
2008
    /**
2009
     * CaseExpression ::= GeneralCaseExpression | SimpleCaseExpression | CoalesceExpression | NullifExpression
2010
     * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END"
2011
     * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression
2012
     * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END"
2013
     * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator
2014
     * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression
2015
     * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
2016
     * NullifExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
2017
     *
2018
     * @return mixed One of the possible expressions or subexpressions.
2019
     */
2020 19
    public function CaseExpression()
2021
    {
2022 19
        $lookahead = $this->lexer->lookahead['type'];
2023
2024
        switch ($lookahead) {
2025 19
            case Lexer::T_NULLIF:
2026 5
                return $this->NullIfExpression();
2027
2028 16
            case Lexer::T_COALESCE:
2029 2
                return $this->CoalesceExpression();
2030
2031 14
            case Lexer::T_CASE:
2032 14
                $this->lexer->resetPeek();
2033 14
                $peek = $this->lexer->peek();
2034
2035 14
                if ($peek['type'] === Lexer::T_WHEN) {
2036 9
                    return $this->GeneralCaseExpression();
2037
                }
2038
2039 5
                return $this->SimpleCaseExpression();
2040
2041
            default:
2042
                // Do nothing
2043
                break;
2044
        }
2045
2046
        $this->syntaxError();
2047
    }
2048
2049
    /**
2050
     * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
2051
     *
2052
     * @return \Doctrine\ORM\Query\AST\CoalesceExpression
2053
     */
2054 3
    public function CoalesceExpression()
2055
    {
2056 3
        $this->match(Lexer::T_COALESCE);
2057 3
        $this->match(Lexer::T_OPEN_PARENTHESIS);
2058
2059
        // Process ScalarExpressions (1..N)
2060 3
        $scalarExpressions = [];
2061 3
        $scalarExpressions[] = $this->ScalarExpression();
2062
2063 3
        while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
2064 3
            $this->match(Lexer::T_COMMA);
2065
2066 3
            $scalarExpressions[] = $this->ScalarExpression();
2067
        }
2068
2069 3
        $this->match(Lexer::T_CLOSE_PARENTHESIS);
2070
2071 3
        return new AST\CoalesceExpression($scalarExpressions);
2072
    }
2073
2074
    /**
2075
     * NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
2076
     *
2077
     * @return \Doctrine\ORM\Query\AST\NullIfExpression
2078
     */
2079 5
    public function NullIfExpression()
2080
    {
2081 5
        $this->match(Lexer::T_NULLIF);
2082 5
        $this->match(Lexer::T_OPEN_PARENTHESIS);
2083
2084 5
        $firstExpression = $this->ScalarExpression();
2085 5
        $this->match(Lexer::T_COMMA);
2086 5
        $secondExpression = $this->ScalarExpression();
2087
2088 5
        $this->match(Lexer::T_CLOSE_PARENTHESIS);
2089
2090 5
        return new AST\NullIfExpression($firstExpression, $secondExpression);
2091
    }
2092
2093
    /**
2094
     * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END"
2095
     *
2096
     * @return \Doctrine\ORM\Query\AST\GeneralCaseExpression
2097
     */
2098 9
    public function GeneralCaseExpression()
2099
    {
2100 9
        $this->match(Lexer::T_CASE);
2101
2102
        // Process WhenClause (1..N)
2103 9
        $whenClauses = [];
2104
2105
        do {
2106 9
            $whenClauses[] = $this->WhenClause();
2107 9
        } while ($this->lexer->isNextToken(Lexer::T_WHEN));
2108
2109 9
        $this->match(Lexer::T_ELSE);
2110 9
        $scalarExpression = $this->ScalarExpression();
2111 9
        $this->match(Lexer::T_END);
2112
2113 9
        return new AST\GeneralCaseExpression($whenClauses, $scalarExpression);
2114
    }
2115
2116
    /**
2117
     * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END"
2118
     * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator
2119
     *
2120
     * @return AST\SimpleCaseExpression
2121
     */
2122 5
    public function SimpleCaseExpression()
2123
    {
2124 5
        $this->match(Lexer::T_CASE);
2125 5
        $caseOperand = $this->StateFieldPathExpression();
2126
2127
        // Process SimpleWhenClause (1..N)
2128 5
        $simpleWhenClauses = [];
2129
2130
        do {
2131 5
            $simpleWhenClauses[] = $this->SimpleWhenClause();
2132 5
        } while ($this->lexer->isNextToken(Lexer::T_WHEN));
2133
2134 5
        $this->match(Lexer::T_ELSE);
2135 5
        $scalarExpression = $this->ScalarExpression();
2136 5
        $this->match(Lexer::T_END);
2137
2138 5
        return new AST\SimpleCaseExpression($caseOperand, $simpleWhenClauses, $scalarExpression);
2139
    }
2140
2141
    /**
2142
     * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression
2143
     *
2144
     * @return \Doctrine\ORM\Query\AST\WhenClause
2145
     */
2146 9
    public function WhenClause()
2147
    {
2148 9
        $this->match(Lexer::T_WHEN);
2149 9
        $conditionalExpression = $this->ConditionalExpression();
2150 9
        $this->match(Lexer::T_THEN);
2151
2152 9
        return new AST\WhenClause($conditionalExpression, $this->ScalarExpression());
2153
    }
2154
2155
    /**
2156
     * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression
2157
     *
2158
     * @return \Doctrine\ORM\Query\AST\SimpleWhenClause
2159
     */
2160 5
    public function SimpleWhenClause()
2161
    {
2162 5
        $this->match(Lexer::T_WHEN);
2163 5
        $conditionalExpression = $this->ScalarExpression();
2164 5
        $this->match(Lexer::T_THEN);
2165
2166 5
        return new AST\SimpleWhenClause($conditionalExpression, $this->ScalarExpression());
2167
    }
2168
2169
    /**
2170
     * SelectExpression ::= (
2171
     *     IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration |
2172
     *     PartialObjectExpression | "(" Subselect ")" | CaseExpression | NewObjectExpression
2173
     * ) [["AS"] ["HIDDEN"] AliasResultVariable]
2174
     *
2175
     * @return \Doctrine\ORM\Query\AST\SelectExpression
2176
     */
2177 756
    public function SelectExpression()
2178
    {
2179 756
        $expression    = null;
2180 756
        $identVariable = null;
2181 756
        $peek          = $this->lexer->glimpse();
2182 756
        $lookaheadType = $this->lexer->lookahead['type'];
2183
2184
        switch (true) {
2185
            // ScalarExpression (u.name)
2186 756
            case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] === Lexer::T_DOT):
2187 102
                $expression = $this->ScalarExpression();
2188 102
                break;
2189
2190
            // IdentificationVariable (u)
2191 697
            case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] !== Lexer::T_OPEN_PARENTHESIS):
2192 582
                $expression = $identVariable = $this->IdentificationVariable();
2193 582
                break;
2194
2195
            // CaseExpression (CASE ... or NULLIF(...) or COALESCE(...))
2196 176
            case ($lookaheadType === Lexer::T_CASE):
2197 171
            case ($lookaheadType === Lexer::T_COALESCE):
2198 169
            case ($lookaheadType === Lexer::T_NULLIF):
2199 9
                $expression = $this->CaseExpression();
2200 9
                break;
2201
2202
            // DQL Function (SUM(u.value) or SUM(u.value) + 1)
2203 167
            case ($this->isFunction()):
2204 87
                $this->lexer->peek(); // "("
2205
2206
                switch (true) {
2207 87
                    case ($this->isMathOperator($this->peekBeyondClosingParenthesis())):
2208
                        // SUM(u.id) + COUNT(u.id)
2209 2
                        $expression = $this->ScalarExpression();
2210 2
                        break;
2211
2212 85
                    case ($this->isAggregateFunction($lookaheadType)):
2213
                        // COUNT(u.id)
2214 54
                        $expression = $this->AggregateExpression();
2215 54
                        break;
2216
2217
                    default:
2218
                        // IDENTITY(u)
2219 31
                        $expression = $this->FunctionDeclaration();
2220 31
                        break;
2221
                }
2222
2223 87
                break;
2224
2225
            // PartialObjectExpression (PARTIAL u.{id, name})
2226 80
            case ($lookaheadType === Lexer::T_PARTIAL):
2227 11
                $expression    = $this->PartialObjectExpression();
2228 11
                $identVariable = $expression->identificationVariable;
2229 11
                break;
2230
2231
            // Subselect
2232 69
            case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS && $peek['type'] === Lexer::T_SELECT):
2233 22
                $this->match(Lexer::T_OPEN_PARENTHESIS);
2234 22
                $expression = $this->Subselect();
2235 22
                $this->match(Lexer::T_CLOSE_PARENTHESIS);
2236 22
                break;
2237
2238
            // Shortcut: ScalarExpression => SimpleArithmeticExpression
2239 47
            case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS):
2240 43
            case ($lookaheadType === Lexer::T_INTEGER):
2241 41
            case ($lookaheadType === Lexer::T_STRING):
2242 32
            case ($lookaheadType === Lexer::T_FLOAT):
2243
            // SimpleArithmeticExpression : (- u.value ) or ( + u.value )
2244 32
            case ($lookaheadType === Lexer::T_MINUS):
2245 32
            case ($lookaheadType === Lexer::T_PLUS):
2246 16
                $expression = $this->SimpleArithmeticExpression();
2247 16
                break;
2248
2249
            // NewObjectExpression (New ClassName(id, name))
2250 31
            case ($lookaheadType === Lexer::T_NEW):
2251 28
                $expression = $this->NewObjectExpression();
2252 28
                break;
2253
2254
            default:
2255 3
                $this->syntaxError(
2256 3
                    'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression',
2257 3
                    $this->lexer->lookahead
2258
                );
2259
        }
2260
2261
        // [["AS"] ["HIDDEN"] AliasResultVariable]
2262 753
        $mustHaveAliasResultVariable = false;
2263
2264 753
        if ($this->lexer->isNextToken(Lexer::T_AS)) {
2265 110
            $this->match(Lexer::T_AS);
2266
2267 110
            $mustHaveAliasResultVariable = true;
2268
        }
2269
2270 753
        $hiddenAliasResultVariable = false;
2271
2272 753
        if ($this->lexer->isNextToken(Lexer::T_HIDDEN)) {
2273 10
            $this->match(Lexer::T_HIDDEN);
2274
2275 10
            $hiddenAliasResultVariable = true;
2276
        }
2277
2278 753
        $aliasResultVariable = null;
2279
2280 753
        if ($mustHaveAliasResultVariable || $this->lexer->isNextToken(Lexer::T_IDENTIFIER)) {
2281 119
            $token = $this->lexer->lookahead;
2282 119
            $aliasResultVariable = $this->AliasResultVariable();
2283
2284
            // Include AliasResultVariable in query components.
2285 114
            $this->queryComponents[$aliasResultVariable] = [
2286 114
                'resultVariable' => $expression,
2287 114
                'nestingLevel'   => $this->nestingLevel,
2288 114
                'token'          => $token,
2289
            ];
2290
        }
2291
2292
        // AST
2293
2294 748
        $expr = new AST\SelectExpression($expression, $aliasResultVariable, $hiddenAliasResultVariable);
2295
2296 748
        if ($identVariable) {
2297 590
            $this->identVariableExpressions[$identVariable] = $expr;
2298
        }
2299
2300 748
        return $expr;
2301
    }
2302
2303
    /**
2304
     * SimpleSelectExpression ::= (
2305
     *      StateFieldPathExpression | IdentificationVariable | FunctionDeclaration |
2306
     *      AggregateExpression | "(" Subselect ")" | ScalarExpression
2307
     * ) [["AS"] AliasResultVariable]
2308
     *
2309
     * @return \Doctrine\ORM\Query\AST\SimpleSelectExpression
2310
     */
2311 48
    public function SimpleSelectExpression()
2312
    {
2313 48
        $peek = $this->lexer->glimpse();
2314
2315 48
        switch ($this->lexer->lookahead['type']) {
2316 48
            case Lexer::T_IDENTIFIER:
2317
                switch (true) {
2318 19
                    case ($peek['type'] === Lexer::T_DOT):
2319 16
                        $expression = $this->StateFieldPathExpression();
2320
2321 16
                        return new AST\SimpleSelectExpression($expression);
2322
2323 3
                    case ($peek['type'] !== Lexer::T_OPEN_PARENTHESIS):
2324 2
                        $expression = $this->IdentificationVariable();
2325
2326 2
                        return new AST\SimpleSelectExpression($expression);
2327
2328 1
                    case ($this->isFunction()):
2329
                        // SUM(u.id) + COUNT(u.id)
2330 1
                        if ($this->isMathOperator($this->peekBeyondClosingParenthesis())) {
2331
                            return new AST\SimpleSelectExpression($this->ScalarExpression());
2332
                        }
2333
                        // COUNT(u.id)
2334 1
                        if ($this->isAggregateFunction($this->lexer->lookahead['type'])) {
2335
                            return new AST\SimpleSelectExpression($this->AggregateExpression());
2336
                        }
2337
                        // IDENTITY(u)
2338 1
                        return new AST\SimpleSelectExpression($this->FunctionDeclaration());
2339
2340
                    default:
2341
                        // Do nothing
2342
                }
2343
                break;
2344
2345 30
            case Lexer::T_OPEN_PARENTHESIS:
2346 3
                if ($peek['type'] !== Lexer::T_SELECT) {
2347
                    // Shortcut: ScalarExpression => SimpleArithmeticExpression
2348 3
                    $expression = $this->SimpleArithmeticExpression();
2349
2350 3
                    return new AST\SimpleSelectExpression($expression);
2351
                }
2352
2353
                // Subselect
2354
                $this->match(Lexer::T_OPEN_PARENTHESIS);
2355
                $expression = $this->Subselect();
2356
                $this->match(Lexer::T_CLOSE_PARENTHESIS);
2357
2358
                return new AST\SimpleSelectExpression($expression);
2359
2360
            default:
2361
                // Do nothing
2362
        }
2363
2364 27
        $this->lexer->peek();
2365
2366 27
        $expression = $this->ScalarExpression();
2367 27
        $expr       = new AST\SimpleSelectExpression($expression);
2368
2369 27
        if ($this->lexer->isNextToken(Lexer::T_AS)) {
2370 1
            $this->match(Lexer::T_AS);
2371
        }
2372
2373 27
        if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER)) {
2374 2
            $token = $this->lexer->lookahead;
2375 2
            $resultVariable = $this->AliasResultVariable();
2376 2
            $expr->fieldIdentificationVariable = $resultVariable;
2377
2378
            // Include AliasResultVariable in query components.
2379 2
            $this->queryComponents[$resultVariable] = [
2380 2
                'resultvariable' => $expr,
2381 2
                'nestingLevel'   => $this->nestingLevel,
2382 2
                'token'          => $token,
2383
            ];
2384
        }
2385
2386 27
        return $expr;
2387
    }
2388
2389
    /**
2390
     * ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}*
2391
     *
2392
     * @return \Doctrine\ORM\Query\AST\ConditionalExpression
2393
     */
2394 371
    public function ConditionalExpression()
2395
    {
2396 371
        $conditionalTerms = [];
2397 371
        $conditionalTerms[] = $this->ConditionalTerm();
2398
2399 368
        while ($this->lexer->isNextToken(Lexer::T_OR)) {
2400 16
            $this->match(Lexer::T_OR);
2401
2402 16
            $conditionalTerms[] = $this->ConditionalTerm();
2403
        }
2404
2405
        // Phase 1 AST optimization: Prevent AST\ConditionalExpression
2406
        // if only one AST\ConditionalTerm is defined
2407 368
        if (count($conditionalTerms) == 1) {
2408 360
            return $conditionalTerms[0];
2409
        }
2410
2411 16
        return new AST\ConditionalExpression($conditionalTerms);
2412
    }
2413
2414
    /**
2415
     * ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}*
2416
     *
2417
     * @return \Doctrine\ORM\Query\AST\ConditionalTerm
2418
     */
2419 371
    public function ConditionalTerm()
2420
    {
2421 371
        $conditionalFactors = [];
2422 371
        $conditionalFactors[] = $this->ConditionalFactor();
2423
2424 368
        while ($this->lexer->isNextToken(Lexer::T_AND)) {
2425 31
            $this->match(Lexer::T_AND);
2426
2427 31
            $conditionalFactors[] = $this->ConditionalFactor();
2428
        }
2429
2430
        // Phase 1 AST optimization: Prevent AST\ConditionalTerm
2431
        // if only one AST\ConditionalFactor is defined
2432 368
        if (count($conditionalFactors) == 1) {
2433 351
            return $conditionalFactors[0];
2434
        }
2435
2436 31
        return new AST\ConditionalTerm($conditionalFactors);
2437
    }
2438
2439
    /**
2440
     * ConditionalFactor ::= ["NOT"] ConditionalPrimary
2441
     *
2442
     * @return \Doctrine\ORM\Query\AST\ConditionalFactor
2443
     */
2444 371
    public function ConditionalFactor()
2445
    {
2446 371
        $not = false;
2447
2448 371
        if ($this->lexer->isNextToken(Lexer::T_NOT)) {
2449 6
            $this->match(Lexer::T_NOT);
2450
2451 6
            $not = true;
2452
        }
2453
2454 371
        $conditionalPrimary = $this->ConditionalPrimary();
2455
2456
        // Phase 1 AST optimization: Prevent AST\ConditionalFactor
2457
        // if only one AST\ConditionalPrimary is defined
2458 368
        if ( ! $not) {
2459 366
            return $conditionalPrimary;
2460
        }
2461
2462 6
        $conditionalFactor = new AST\ConditionalFactor($conditionalPrimary);
2463 6
        $conditionalFactor->not = $not;
2464
2465 6
        return $conditionalFactor;
2466
    }
2467
2468
    /**
2469
     * ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")"
2470
     *
2471
     * @return \Doctrine\ORM\Query\AST\ConditionalPrimary
2472
     */
2473 371
    public function ConditionalPrimary()
2474
    {
2475 371
        $condPrimary = new AST\ConditionalPrimary;
2476
2477 371
        if ( ! $this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
2478 362
            $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression();
2479
2480 359
            return $condPrimary;
2481
        }
2482
2483
        // Peek beyond the matching closing parenthesis ')'
2484 25
        $peek = $this->peekBeyondClosingParenthesis();
2485
2486 25
        if (in_array($peek['value'], ["=",  "<", "<=", "<>", ">", ">=", "!="]) ||
2487 22
            in_array($peek['type'], [Lexer::T_NOT, Lexer::T_BETWEEN, Lexer::T_LIKE, Lexer::T_IN, Lexer::T_IS, Lexer::T_EXISTS]) ||
2488 25
            $this->isMathOperator($peek)) {
2489 15
            $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression();
2490
2491 15
            return $condPrimary;
2492
        }
2493
2494 21
        $this->match(Lexer::T_OPEN_PARENTHESIS);
2495 21
        $condPrimary->conditionalExpression = $this->ConditionalExpression();
2496 21
        $this->match(Lexer::T_CLOSE_PARENTHESIS);
2497
2498 21
        return $condPrimary;
2499
    }
2500
2501
    /**
2502
     * SimpleConditionalExpression ::=
2503
     *      ComparisonExpression | BetweenExpression | LikeExpression |
2504
     *      InExpression | NullComparisonExpression | ExistsExpression |
2505
     *      EmptyCollectionComparisonExpression | CollectionMemberExpression |
2506
     *      InstanceOfExpression
2507
     */
2508 371
    public function SimpleConditionalExpression()
2509
    {
2510 371
        if ($this->lexer->isNextToken(Lexer::T_EXISTS)) {
2511 7
            return $this->ExistsExpression();
2512
        }
2513
2514 371
        $token      = $this->lexer->lookahead;
2515 371
        $peek       = $this->lexer->glimpse();
2516 371
        $lookahead  = $token;
2517
2518 371
        if ($this->lexer->isNextToken(Lexer::T_NOT)) {
2519
            $token = $this->lexer->glimpse();
2520
        }
2521
2522 371
        if ($token['type'] === Lexer::T_IDENTIFIER || $token['type'] === Lexer::T_INPUT_PARAMETER || $this->isFunction()) {
2523
            // Peek beyond the matching closing parenthesis.
2524 347
            $beyond = $this->lexer->peek();
2525
2526 347
            switch ($peek['value']) {
2527 347
                case '(':
2528
                    // Peeks beyond the matched closing parenthesis.
2529 33
                    $token = $this->peekBeyondClosingParenthesis(false);
2530
2531 33
                    if ($token['type'] === Lexer::T_NOT) {
2532 3
                        $token = $this->lexer->peek();
2533
                    }
2534
2535 33
                    if ($token['type'] === Lexer::T_IS) {
2536 2
                        $lookahead = $this->lexer->peek();
2537
                    }
2538 33
                    break;
2539
2540
                default:
2541
                    // Peek beyond the PathExpression or InputParameter.
2542 320
                    $token = $beyond;
2543
2544 320
                    while ($token['value'] === '.') {
2545 281
                        $this->lexer->peek();
2546
2547 281
                        $token = $this->lexer->peek();
2548
                    }
2549
2550
                    // Also peek beyond a NOT if there is one.
2551 320
                    if ($token['type'] === Lexer::T_NOT) {
2552 11
                        $token = $this->lexer->peek();
2553
                    }
2554
2555
                    // We need to go even further in case of IS (differentiate between NULL and EMPTY)
2556 320
                    $lookahead = $this->lexer->peek();
2557
            }
2558
2559
            // Also peek beyond a NOT if there is one.
2560 347
            if ($lookahead['type'] === Lexer::T_NOT) {
2561 7
                $lookahead = $this->lexer->peek();
2562
            }
2563
2564 347
            $this->lexer->resetPeek();
2565
        }
2566
2567 371
        if ($token['type'] === Lexer::T_BETWEEN) {
2568 8
            return $this->BetweenExpression();
2569
        }
2570
2571 365
        if ($token['type'] === Lexer::T_LIKE) {
2572 14
            return $this->LikeExpression();
2573
        }
2574
2575 352
        if ($token['type'] === Lexer::T_IN) {
2576 34
            return $this->InExpression();
2577
        }
2578
2579 327
        if ($token['type'] === Lexer::T_INSTANCE) {
2580 12
            return $this->InstanceOfExpression();
2581
        }
2582
2583 315
        if ($token['type'] === Lexer::T_MEMBER) {
2584 7
            return $this->CollectionMemberExpression();
2585
        }
2586
2587 308
        if ($token['type'] === Lexer::T_IS && $lookahead['type'] === Lexer::T_NULL) {
2588 14
            return $this->NullComparisonExpression();
2589
        }
2590
2591 297
        if ($token['type'] === Lexer::T_IS  && $lookahead['type'] === Lexer::T_EMPTY) {
2592 4
            return $this->EmptyCollectionComparisonExpression();
2593
        }
2594
2595 293
        return $this->ComparisonExpression();
2596
    }
2597
2598
    /**
2599
     * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY"
2600
     *
2601
     * @return \Doctrine\ORM\Query\AST\EmptyCollectionComparisonExpression
2602
     */
2603 4
    public function EmptyCollectionComparisonExpression()
2604
    {
2605 4
        $emptyCollectionCompExpr = new AST\EmptyCollectionComparisonExpression(
2606 4
            $this->CollectionValuedPathExpression()
2607
        );
2608 4
        $this->match(Lexer::T_IS);
2609
2610 4
        if ($this->lexer->isNextToken(Lexer::T_NOT)) {
2611 2
            $this->match(Lexer::T_NOT);
2612 2
            $emptyCollectionCompExpr->not = true;
2613
        }
2614
2615 4
        $this->match(Lexer::T_EMPTY);
2616
2617 4
        return $emptyCollectionCompExpr;
2618
    }
2619
2620
    /**
2621
     * CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression
2622
     *
2623
     * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression
2624
     * SimpleEntityExpression ::= IdentificationVariable | InputParameter
2625
     *
2626
     * @return \Doctrine\ORM\Query\AST\CollectionMemberExpression
2627
     */
2628 7
    public function CollectionMemberExpression()
2629
    {
2630 7
        $not        = false;
2631 7
        $entityExpr = $this->EntityExpression();
2632
2633 7
        if ($this->lexer->isNextToken(Lexer::T_NOT)) {
2634
            $this->match(Lexer::T_NOT);
2635
2636
            $not = true;
2637
        }
2638
2639 7
        $this->match(Lexer::T_MEMBER);
2640
2641 7
        if ($this->lexer->isNextToken(Lexer::T_OF)) {
2642 7
            $this->match(Lexer::T_OF);
2643
        }
2644
2645 7
        $collMemberExpr = new AST\CollectionMemberExpression(
2646 7
            $entityExpr, $this->CollectionValuedPathExpression()
2647
        );
2648 7
        $collMemberExpr->not = $not;
2649
2650 7
        return $collMemberExpr;
2651
    }
2652
2653
    /**
2654
     * Literal ::= string | char | integer | float | boolean
2655
     *
2656
     * @return \Doctrine\ORM\Query\AST\Literal
2657
     */
2658 177
    public function Literal()
2659
    {
2660 177
        switch ($this->lexer->lookahead['type']) {
2661 177
            case Lexer::T_STRING:
2662 48
                $this->match(Lexer::T_STRING);
2663
2664 48
                return new AST\Literal(AST\Literal::STRING, $this->lexer->token['value']);
2665 137
            case Lexer::T_INTEGER:
2666 9
            case Lexer::T_FLOAT:
2667 129
                $this->match(
2668 129
                    $this->lexer->isNextToken(Lexer::T_INTEGER) ? Lexer::T_INTEGER : Lexer::T_FLOAT
2669
                );
2670
2671 129
                return new AST\Literal(AST\Literal::NUMERIC, $this->lexer->token['value']);
2672 8
            case Lexer::T_TRUE:
2673 4
            case Lexer::T_FALSE:
2674 8
                $this->match(
2675 8
                    $this->lexer->isNextToken(Lexer::T_TRUE) ? Lexer::T_TRUE : Lexer::T_FALSE
2676
                );
2677
2678 8
                return new AST\Literal(AST\Literal::BOOLEAN, $this->lexer->token['value']);
2679
            default:
2680
                $this->syntaxError('Literal');
2681
        }
2682
    }
2683
2684
    /**
2685
     * InParameter ::= Literal | InputParameter
2686
     *
2687
     * @return string | \Doctrine\ORM\Query\AST\InputParameter
2688
     */
2689 25
    public function InParameter()
2690
    {
2691 25
        if ($this->lexer->lookahead['type'] == Lexer::T_INPUT_PARAMETER) {
2692 13
            return $this->InputParameter();
2693
        }
2694
2695 12
        return $this->Literal();
2696
    }
2697
2698
    /**
2699
     * InputParameter ::= PositionalParameter | NamedParameter
2700
     *
2701
     * @return \Doctrine\ORM\Query\AST\InputParameter
2702
     */
2703 161
    public function InputParameter()
2704
    {
2705 161
        $this->match(Lexer::T_INPUT_PARAMETER);
2706
2707 161
        return new AST\InputParameter($this->lexer->token['value']);
2708
    }
2709
2710
    /**
2711
     * ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")"
2712
     *
2713
     * @return \Doctrine\ORM\Query\AST\ArithmeticExpression
2714
     */
2715 326
    public function ArithmeticExpression()
2716
    {
2717 326
        $expr = new AST\ArithmeticExpression;
2718
2719 326
        if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
2720 19
            $peek = $this->lexer->glimpse();
2721
2722 19
            if ($peek['type'] === Lexer::T_SELECT) {
2723 7
                $this->match(Lexer::T_OPEN_PARENTHESIS);
2724 7
                $expr->subselect = $this->Subselect();
2725 7
                $this->match(Lexer::T_CLOSE_PARENTHESIS);
2726
2727 7
                return $expr;
2728
            }
2729
        }
2730
2731 326
        $expr->simpleArithmeticExpression = $this->SimpleArithmeticExpression();
2732
2733 326
        return $expr;
2734
    }
2735
2736
    /**
2737
     * SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}*
2738
     *
2739
     * @return \Doctrine\ORM\Query\AST\SimpleArithmeticExpression
2740
     */
2741 429
    public function SimpleArithmeticExpression()
2742
    {
2743 429
        $terms = [];
2744 429
        $terms[] = $this->ArithmeticTerm();
2745
2746 429
        while (($isPlus = $this->lexer->isNextToken(Lexer::T_PLUS)) || $this->lexer->isNextToken(Lexer::T_MINUS)) {
2747 21
            $this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS);
2748
2749 21
            $terms[] = $this->lexer->token['value'];
2750 21
            $terms[] = $this->ArithmeticTerm();
2751
        }
2752
2753
        // Phase 1 AST optimization: Prevent AST\SimpleArithmeticExpression
2754
        // if only one AST\ArithmeticTerm is defined
2755 429
        if (count($terms) == 1) {
2756 424
            return $terms[0];
2757
        }
2758
2759 21
        return new AST\SimpleArithmeticExpression($terms);
2760
    }
2761
2762
    /**
2763
     * ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}*
2764
     *
2765
     * @return \Doctrine\ORM\Query\AST\ArithmeticTerm
2766
     */
2767 429
    public function ArithmeticTerm()
2768
    {
2769 429
        $factors = [];
2770 429
        $factors[] = $this->ArithmeticFactor();
2771
2772 429
        while (($isMult = $this->lexer->isNextToken(Lexer::T_MULTIPLY)) || $this->lexer->isNextToken(Lexer::T_DIVIDE)) {
2773 53
            $this->match(($isMult) ? Lexer::T_MULTIPLY : Lexer::T_DIVIDE);
2774
2775 53
            $factors[] = $this->lexer->token['value'];
2776 53
            $factors[] = $this->ArithmeticFactor();
2777
        }
2778
2779
        // Phase 1 AST optimization: Prevent AST\ArithmeticTerm
2780
        // if only one AST\ArithmeticFactor is defined
2781 429
        if (count($factors) == 1) {
2782 401
            return $factors[0];
2783
        }
2784
2785 53
        return new AST\ArithmeticTerm($factors);
2786
    }
2787
2788
    /**
2789
     * ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary
2790
     *
2791
     * @return \Doctrine\ORM\Query\AST\ArithmeticFactor
2792
     */
2793 429
    public function ArithmeticFactor()
2794
    {
2795 429
        $sign = null;
2796
2797 429
        if (($isPlus = $this->lexer->isNextToken(Lexer::T_PLUS)) || $this->lexer->isNextToken(Lexer::T_MINUS)) {
2798 3
            $this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS);
2799 3
            $sign = $isPlus;
2800
        }
2801
2802 429
        $primary = $this->ArithmeticPrimary();
2803
2804
        // Phase 1 AST optimization: Prevent AST\ArithmeticFactor
2805
        // if only one AST\ArithmeticPrimary is defined
2806 429
        if ($sign === null) {
2807 428
            return $primary;
2808
        }
2809
2810 3
        return new AST\ArithmeticFactor($primary, $sign);
2811
    }
2812
2813
    /**
2814
     * ArithmeticPrimary ::= SingleValuedPathExpression | Literal | ParenthesisExpression
2815
     *          | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings
2816
     *          | FunctionsReturningDatetime | IdentificationVariable | ResultVariable
2817
     *          | InputParameter | CaseExpression
2818
     */
2819 433
    public function ArithmeticPrimary()
2820
    {
2821 433
        if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
2822 25
            $this->match(Lexer::T_OPEN_PARENTHESIS);
2823
2824 25
            $expr = $this->SimpleArithmeticExpression();
2825
2826 25
            $this->match(Lexer::T_CLOSE_PARENTHESIS);
2827
2828 25
            return new AST\ParenthesisExpression($expr);
2829
        }
2830
2831 433
        switch ($this->lexer->lookahead['type']) {
2832 433
            case Lexer::T_COALESCE:
2833 433
            case Lexer::T_NULLIF:
2834 433
            case Lexer::T_CASE:
2835 4
                return $this->CaseExpression();
2836
2837 433
            case Lexer::T_IDENTIFIER:
2838 403
                $peek = $this->lexer->glimpse();
2839
2840 403
                if ($peek['value'] == '(') {
2841 28
                    return $this->FunctionDeclaration();
2842
                }
2843
2844 383
                if ($peek['value'] == '.') {
2845 372
                    return $this->SingleValuedPathExpression();
2846
                }
2847
2848 46
                if (isset($this->queryComponents[$this->lexer->lookahead['value']]['resultVariable'])) {
2849 10
                    return $this->ResultVariable();
2850
                }
2851
2852 38
                return $this->StateFieldPathExpression();
2853
2854 306
            case Lexer::T_INPUT_PARAMETER:
2855 143
                return $this->InputParameter();
2856
2857
            default:
2858 171
                $peek = $this->lexer->glimpse();
2859
2860 171
                if ($peek['value'] == '(') {
2861 18
                    if ($this->isAggregateFunction($this->lexer->lookahead['type'])) {
2862 18
                        return $this->AggregateExpression();
2863
                    }
2864
2865
                    return $this->FunctionDeclaration();
2866
                }
2867
2868 167
                return $this->Literal();
2869
        }
2870
    }
2871
2872
    /**
2873
     * StringExpression ::= StringPrimary | ResultVariable | "(" Subselect ")"
2874
     *
2875
     * @return \Doctrine\ORM\Query\AST\Subselect |
2876
     *         string
2877
     */
2878 14
    public function StringExpression()
2879
    {
2880 14
        $peek = $this->lexer->glimpse();
2881
2882
        // Subselect
2883 14
        if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS) && $peek['type'] === Lexer::T_SELECT) {
2884
            $this->match(Lexer::T_OPEN_PARENTHESIS);
2885
            $expr = $this->Subselect();
2886
            $this->match(Lexer::T_CLOSE_PARENTHESIS);
2887
2888
            return $expr;
2889
        }
2890
2891
        // ResultVariable (string)
2892 14
        if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER) &&
2893 14
            isset($this->queryComponents[$this->lexer->lookahead['value']]['resultVariable'])) {
2894 2
            return $this->ResultVariable();
2895
        }
2896
2897 12
        return $this->StringPrimary();
2898
    }
2899
2900
    /**
2901
     * StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression
2902
     */
2903 51
    public function StringPrimary()
2904
    {
2905 51
        $lookaheadType = $this->lexer->lookahead['type'];
2906
2907
        switch ($lookaheadType) {
2908 51
            case Lexer::T_IDENTIFIER:
2909 32
                $peek = $this->lexer->glimpse();
2910
2911 32
                if ($peek['value'] == '.') {
2912 32
                    return $this->StateFieldPathExpression();
2913
                }
2914
2915 8
                if ($peek['value'] == '(') {
2916
                    // do NOT directly go to FunctionsReturningString() because it doesn't check for custom functions.
2917 8
                    return $this->FunctionDeclaration();
2918
                }
2919
2920
                $this->syntaxError("'.' or '('");
2921
                break;
2922
2923 32
            case Lexer::T_STRING:
2924 32
                $this->match(Lexer::T_STRING);
2925
2926 32
                return new AST\Literal(AST\Literal::STRING, $this->lexer->token['value']);
2927
2928 2
            case Lexer::T_INPUT_PARAMETER:
2929 2
                return $this->InputParameter();
2930
2931
            case Lexer::T_CASE:
2932
            case Lexer::T_COALESCE:
2933
            case Lexer::T_NULLIF:
2934
                return $this->CaseExpression();
2935
2936
            default:
2937
                if ($this->isAggregateFunction($lookaheadType)) {
2938
                    return $this->AggregateExpression();
2939
                }
2940
        }
2941
2942
        $this->syntaxError(
2943
            'StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression'
2944
        );
2945
    }
2946
2947
    /**
2948
     * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression
2949
     *
2950
     * @return \Doctrine\ORM\Query\AST\PathExpression |
2951
     *         \Doctrine\ORM\Query\AST\SimpleEntityExpression
2952
     */
2953 7
    public function EntityExpression()
2954
    {
2955 7
        $glimpse = $this->lexer->glimpse();
2956
2957 7
        if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER) && $glimpse['value'] === '.') {
2958 1
            return $this->SingleValuedAssociationPathExpression();
2959
        }
2960
2961 6
        return $this->SimpleEntityExpression();
2962
    }
2963
2964
    /**
2965
     * SimpleEntityExpression ::= IdentificationVariable | InputParameter
2966
     *
2967
     * @return string | \Doctrine\ORM\Query\AST\InputParameter
2968
     */
2969 6
    public function SimpleEntityExpression()
2970
    {
2971 6
        if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
2972 5
            return $this->InputParameter();
2973
        }
2974
2975 1
        return $this->StateFieldPathExpression();
2976
    }
2977
2978
    /**
2979
     * AggregateExpression ::=
2980
     *  ("AVG" | "MAX" | "MIN" | "SUM" | "COUNT") "(" ["DISTINCT"] SimpleArithmeticExpression ")"
2981
     *
2982
     * @return \Doctrine\ORM\Query\AST\AggregateExpression
2983
     */
2984 84
    public function AggregateExpression()
2985
    {
2986 84
        $lookaheadType = $this->lexer->lookahead['type'];
2987 84
        $isDistinct = false;
2988
2989 84
        if ( ! in_array($lookaheadType, [Lexer::T_COUNT, Lexer::T_AVG, Lexer::T_MAX, Lexer::T_MIN, Lexer::T_SUM])) {
2990
            $this->syntaxError('One of: MAX, MIN, AVG, SUM, COUNT');
2991
        }
2992
2993 84
        $this->match($lookaheadType);
2994 84
        $functionName = $this->lexer->token['value'];
2995 84
        $this->match(Lexer::T_OPEN_PARENTHESIS);
2996
2997 84
        if ($this->lexer->isNextToken(Lexer::T_DISTINCT)) {
2998 2
            $this->match(Lexer::T_DISTINCT);
2999 2
            $isDistinct = true;
3000
        }
3001
3002 84
        $pathExp = $this->SimpleArithmeticExpression();
3003
3004 84
        $this->match(Lexer::T_CLOSE_PARENTHESIS);
3005
3006 84
        return new AST\AggregateExpression($functionName, $pathExp, $isDistinct);
3007
    }
3008
3009
    /**
3010
     * QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")"
3011
     *
3012
     * @return \Doctrine\ORM\Query\AST\QuantifiedExpression
3013
     */
3014 3
    public function QuantifiedExpression()
3015
    {
3016 3
        $lookaheadType = $this->lexer->lookahead['type'];
3017 3
        $value = $this->lexer->lookahead['value'];
3018
3019 3
        if ( ! in_array($lookaheadType, [Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME])) {
3020
            $this->syntaxError('ALL, ANY or SOME');
3021
        }
3022
3023 3
        $this->match($lookaheadType);
3024 3
        $this->match(Lexer::T_OPEN_PARENTHESIS);
3025
3026 3
        $qExpr = new AST\QuantifiedExpression($this->Subselect());
3027 3
        $qExpr->type = $value;
3028
3029 3
        $this->match(Lexer::T_CLOSE_PARENTHESIS);
3030
3031 3
        return $qExpr;
3032
    }
3033
3034
    /**
3035
     * BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression
3036
     *
3037
     * @return \Doctrine\ORM\Query\AST\BetweenExpression
3038
     */
3039 8
    public function BetweenExpression()
3040
    {
3041 8
        $not = false;
3042 8
        $arithExpr1 = $this->ArithmeticExpression();
3043
3044 8
        if ($this->lexer->isNextToken(Lexer::T_NOT)) {
3045 3
            $this->match(Lexer::T_NOT);
3046 3
            $not = true;
3047
        }
3048
3049 8
        $this->match(Lexer::T_BETWEEN);
3050 8
        $arithExpr2 = $this->ArithmeticExpression();
3051 8
        $this->match(Lexer::T_AND);
3052 8
        $arithExpr3 = $this->ArithmeticExpression();
3053
3054 8
        $betweenExpr = new AST\BetweenExpression($arithExpr1, $arithExpr2, $arithExpr3);
3055 8
        $betweenExpr->not = $not;
3056
3057 8
        return $betweenExpr;
3058
    }
3059
3060
    /**
3061
     * ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression )
3062
     *
3063
     * @return \Doctrine\ORM\Query\AST\ComparisonExpression
3064
     */
3065 293
    public function ComparisonExpression()
3066
    {
3067 293
        $this->lexer->glimpse();
3068
3069 293
        $leftExpr  = $this->ArithmeticExpression();
3070 293
        $operator  = $this->ComparisonOperator();
3071 293
        $rightExpr = ($this->isNextAllAnySome())
3072 3
            ? $this->QuantifiedExpression()
3073 293
            : $this->ArithmeticExpression();
3074
3075 291
        return new AST\ComparisonExpression($leftExpr, $operator, $rightExpr);
3076
    }
3077
3078
    /**
3079
     * InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
3080
     *
3081
     * @return \Doctrine\ORM\Query\AST\InExpression
3082
     */
3083 34
    public function InExpression()
3084
    {
3085 34
        $inExpression = new AST\InExpression($this->ArithmeticExpression());
3086
3087 34
        if ($this->lexer->isNextToken(Lexer::T_NOT)) {
3088 6
            $this->match(Lexer::T_NOT);
3089 6
            $inExpression->not = true;
3090
        }
3091
3092 34
        $this->match(Lexer::T_IN);
3093 34
        $this->match(Lexer::T_OPEN_PARENTHESIS);
3094
3095 34
        if ($this->lexer->isNextToken(Lexer::T_SELECT)) {
3096 9
            $inExpression->subselect = $this->Subselect();
3097
        } else {
3098 25
            $literals = [];
3099 25
            $literals[] = $this->InParameter();
3100
3101 25
            while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
3102 16
                $this->match(Lexer::T_COMMA);
3103 16
                $literals[] = $this->InParameter();
3104
            }
3105
3106 25
            $inExpression->literals = $literals;
3107
        }
3108
3109 33
        $this->match(Lexer::T_CLOSE_PARENTHESIS);
3110
3111 33
        return $inExpression;
3112
    }
3113
3114
    /**
3115
     * InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")")
3116
     *
3117
     * @return \Doctrine\ORM\Query\AST\InstanceOfExpression
3118
     */
3119 12
    public function InstanceOfExpression()
3120
    {
3121 12
        $instanceOfExpression = new AST\InstanceOfExpression($this->IdentificationVariable());
3122
3123 12
        if ($this->lexer->isNextToken(Lexer::T_NOT)) {
3124 1
            $this->match(Lexer::T_NOT);
3125 1
            $instanceOfExpression->not = true;
3126
        }
3127
3128 12
        $this->match(Lexer::T_INSTANCE);
3129 12
        $this->match(Lexer::T_OF);
3130
3131 12
        $exprValues = [];
3132
3133 12
        if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
3134 1
            $this->match(Lexer::T_OPEN_PARENTHESIS);
3135
3136 1
            $exprValues[] = $this->InstanceOfParameter();
3137
3138 1
            while ($this->lexer->isNextToken(Lexer::T_COMMA)) {
3139 1
                $this->match(Lexer::T_COMMA);
3140
3141 1
                $exprValues[] = $this->InstanceOfParameter();
3142
            }
3143
3144 1
            $this->match(Lexer::T_CLOSE_PARENTHESIS);
3145
3146 1
            $instanceOfExpression->value = $exprValues;
3147
3148 1
            return $instanceOfExpression;
3149
        }
3150
3151 11
        $exprValues[] = $this->InstanceOfParameter();
3152
3153 11
        $instanceOfExpression->value = $exprValues;
3154
3155 11
        return $instanceOfExpression;
3156
    }
3157
3158
    /**
3159
     * InstanceOfParameter ::= AbstractSchemaName | InputParameter
3160
     *
3161
     * @return mixed
3162
     */
3163 12
    public function InstanceOfParameter()
3164
    {
3165 12
        if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
3166 5
            $this->match(Lexer::T_INPUT_PARAMETER);
3167
3168 5
            return new AST\InputParameter($this->lexer->token['value']);
3169
        }
3170
3171 7
        $abstractSchemaName = $this->AbstractSchemaName();
3172
3173 7
        $this->validateAbstractSchemaName($abstractSchemaName);
3174
3175 7
        return $abstractSchemaName;
3176
    }
3177
3178
    /**
3179
     * LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char]
3180
     *
3181
     * @return \Doctrine\ORM\Query\AST\LikeExpression
3182
     */
3183 14
    public function LikeExpression()
3184
    {
3185 14
        $stringExpr = $this->StringExpression();
3186 14
        $not = false;
3187
3188 14
        if ($this->lexer->isNextToken(Lexer::T_NOT)) {
3189 3
            $this->match(Lexer::T_NOT);
3190 3
            $not = true;
3191
        }
3192
3193 14
        $this->match(Lexer::T_LIKE);
3194
3195 14
        if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
3196 4
            $this->match(Lexer::T_INPUT_PARAMETER);
3197 4
            $stringPattern = new AST\InputParameter($this->lexer->token['value']);
3198
        } else {
3199 11
            $stringPattern = $this->StringPrimary();
3200
        }
3201
3202 14
        $escapeChar = null;
3203
3204 14
        if ($this->lexer->lookahead['type'] === Lexer::T_ESCAPE) {
3205 2
            $this->match(Lexer::T_ESCAPE);
3206 2
            $this->match(Lexer::T_STRING);
3207
3208 2
            $escapeChar = new AST\Literal(AST\Literal::STRING, $this->lexer->token['value']);
3209
        }
3210
3211 14
        $likeExpr = new AST\LikeExpression($stringExpr, $stringPattern, $escapeChar);
3212 14
        $likeExpr->not = $not;
3213
3214 14
        return $likeExpr;
3215
    }
3216
3217
    /**
3218
     * NullComparisonExpression ::= (InputParameter | NullIfExpression | CoalesceExpression | AggregateExpression | FunctionDeclaration | IdentificationVariable | SingleValuedPathExpression | ResultVariable) "IS" ["NOT"] "NULL"
3219
     *
3220
     * @return \Doctrine\ORM\Query\AST\NullComparisonExpression
3221
     */
3222 14
    public function NullComparisonExpression()
3223
    {
3224
        switch (true) {
3225 14
            case $this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER):
3226
                $this->match(Lexer::T_INPUT_PARAMETER);
3227
3228
                $expr = new AST\InputParameter($this->lexer->token['value']);
3229
                break;
3230
3231 14
            case $this->lexer->isNextToken(Lexer::T_NULLIF):
3232 1
                $expr = $this->NullIfExpression();
3233 1
                break;
3234
3235 14
            case $this->lexer->isNextToken(Lexer::T_COALESCE):
3236 1
                $expr = $this->CoalesceExpression();
3237 1
                break;
3238
3239 14
            case $this->isAggregateFunction($this->lexer->lookahead['type']):
3240 1
                $expr = $this->AggregateExpression();
3241 1
                break;
3242
3243 14
            case $this->isFunction():
3244 1
                $expr = $this->FunctionDeclaration();
3245 1
                break;
3246
3247
            default:
3248
                // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression
3249 13
                $glimpse = $this->lexer->glimpse();
3250
3251 13
                if ($glimpse['type'] === Lexer::T_DOT) {
3252 9
                    $expr = $this->SingleValuedPathExpression();
3253
3254
                    // Leave switch statement
3255 9
                    break;
3256
                }
3257
3258 4
                $lookaheadValue = $this->lexer->lookahead['value'];
3259
3260
                // Validate existing component
3261 4
                if ( ! isset($this->queryComponents[$lookaheadValue])) {
3262
                    $this->semanticalError('Cannot add having condition on undefined result variable.');
3263
                }
3264
3265
                // Validate SingleValuedPathExpression (ie.: "product")
3266 4
                if (isset($this->queryComponents[$lookaheadValue]['metadata'])) {
3267 1
                    $expr = $this->SingleValuedPathExpression();
3268 1
                    break;
3269
                }
3270
3271
                // Validating ResultVariable
3272 3
                if ( ! isset($this->queryComponents[$lookaheadValue]['resultVariable'])) {
3273
                    $this->semanticalError('Cannot add having condition on a non result variable.');
3274
                }
3275
3276 3
                $expr = $this->ResultVariable();
3277 3
                break;
3278
        }
3279
3280 14
        $nullCompExpr = new AST\NullComparisonExpression($expr);
3281
3282 14
        $this->match(Lexer::T_IS);
3283
3284 14
        if ($this->lexer->isNextToken(Lexer::T_NOT)) {
3285 5
            $this->match(Lexer::T_NOT);
3286
3287 5
            $nullCompExpr->not = true;
3288
        }
3289
3290 14
        $this->match(Lexer::T_NULL);
3291
3292 14
        return $nullCompExpr;
3293
    }
3294
3295
    /**
3296
     * ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")"
3297
     *
3298
     * @return \Doctrine\ORM\Query\AST\ExistsExpression
3299
     */
3300 7
    public function ExistsExpression()
3301
    {
3302 7
        $not = false;
3303
3304 7
        if ($this->lexer->isNextToken(Lexer::T_NOT)) {
3305
            $this->match(Lexer::T_NOT);
3306
            $not = true;
3307
        }
3308
3309 7
        $this->match(Lexer::T_EXISTS);
3310 7
        $this->match(Lexer::T_OPEN_PARENTHESIS);
3311
3312 7
        $existsExpression = new AST\ExistsExpression($this->Subselect());
3313 7
        $existsExpression->not = $not;
3314
3315 7
        $this->match(Lexer::T_CLOSE_PARENTHESIS);
3316
3317 7
        return $existsExpression;
3318
    }
3319
3320
    /**
3321
     * ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!="
3322
     *
3323
     * @return string
3324
     */
3325 293
    public function ComparisonOperator()
3326
    {
3327 293
        switch ($this->lexer->lookahead['value']) {
3328 293
            case '=':
3329 242
                $this->match(Lexer::T_EQUALS);
3330
3331 242
                return '=';
3332
3333 62
            case '<':
3334 17
                $this->match(Lexer::T_LOWER_THAN);
3335 17
                $operator = '<';
3336
3337 17
                if ($this->lexer->isNextToken(Lexer::T_EQUALS)) {
3338 5
                    $this->match(Lexer::T_EQUALS);
3339 5
                    $operator .= '=';
3340 12
                } else if ($this->lexer->isNextToken(Lexer::T_GREATER_THAN)) {
3341 3
                    $this->match(Lexer::T_GREATER_THAN);
3342 3
                    $operator .= '>';
3343
                }
3344
3345 17
                return $operator;
3346
3347 53
            case '>':
3348 47
                $this->match(Lexer::T_GREATER_THAN);
3349 47
                $operator = '>';
3350
3351 47
                if ($this->lexer->isNextToken(Lexer::T_EQUALS)) {
3352 6
                    $this->match(Lexer::T_EQUALS);
3353 6
                    $operator .= '=';
3354
                }
3355
3356 47
                return $operator;
3357
3358 6
            case '!':
3359 6
                $this->match(Lexer::T_NEGATE);
3360 6
                $this->match(Lexer::T_EQUALS);
3361
3362 6
                return '<>';
3363
3364
            default:
3365
                $this->syntaxError('=, <, <=, <>, >, >=, !=');
3366
        }
3367
    }
3368
3369
    /**
3370
     * FunctionDeclaration ::= FunctionsReturningStrings | FunctionsReturningNumerics | FunctionsReturningDatetime
3371
     *
3372
     * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode
3373
     */
3374 58
    public function FunctionDeclaration()
3375
    {
3376 58
        $token = $this->lexer->lookahead;
3377 58
        $funcName = strtolower($token['value']);
3378
3379
        // Check for built-in functions first!
3380
        switch (true) {
3381 58
            case (isset(self::$_STRING_FUNCTIONS[$funcName])):
3382 30
                return $this->FunctionsReturningStrings();
3383
3384 31
            case (isset(self::$_NUMERIC_FUNCTIONS[$funcName])):
3385 22
                return $this->FunctionsReturningNumerics();
3386
3387 10
            case (isset(self::$_DATETIME_FUNCTIONS[$funcName])):
3388 7
                return $this->FunctionsReturningDatetime();
3389
3390
            default:
3391 3
                return $this->CustomFunctionDeclaration();
3392
        }
3393
    }
3394
3395
    /**
3396
     * Helper function for FunctionDeclaration grammar rule.
3397
     *
3398
     * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode
3399
     */
3400 3
    private function CustomFunctionDeclaration()
3401
    {
3402 3
        $token = $this->lexer->lookahead;
3403 3
        $funcName = strtolower($token['value']);
3404
3405
        // Check for custom functions afterwards
3406 3
        $config = $this->em->getConfiguration();
3407
3408
        switch (true) {
3409 3
            case ($config->getCustomStringFunction($funcName) !== null):
3410 2
                return $this->CustomFunctionsReturningStrings();
3411
3412 2
            case ($config->getCustomNumericFunction($funcName) !== null):
3413 2
                return $this->CustomFunctionsReturningNumerics();
3414
3415
            case ($config->getCustomDatetimeFunction($funcName) !== null):
3416
                return $this->CustomFunctionsReturningDatetime();
3417
3418
            default:
3419
                $this->syntaxError('known function', $token);
3420
        }
3421
    }
3422
3423
    /**
3424
     * FunctionsReturningNumerics ::=
3425
     *      "LENGTH" "(" StringPrimary ")" |
3426
     *      "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" |
3427
     *      "ABS" "(" SimpleArithmeticExpression ")" |
3428
     *      "SQRT" "(" SimpleArithmeticExpression ")" |
3429
     *      "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" |
3430
     *      "SIZE" "(" CollectionValuedPathExpression ")" |
3431
     *      "DATE_DIFF" "(" ArithmeticPrimary "," ArithmeticPrimary ")" |
3432
     *      "BIT_AND" "(" ArithmeticPrimary "," ArithmeticPrimary ")" |
3433
     *      "BIT_OR" "(" ArithmeticPrimary "," ArithmeticPrimary ")"
3434
     *
3435
     * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode
3436
     */
3437 22
    public function FunctionsReturningNumerics()
3438
    {
3439 22
        $funcNameLower = strtolower($this->lexer->lookahead['value']);
3440 22
        $funcClass     = self::$_NUMERIC_FUNCTIONS[$funcNameLower];
3441
3442 22
        $function = new $funcClass($funcNameLower);
3443 22
        $function->parse($this);
3444
3445 22
        return $function;
3446
    }
3447
3448
    /**
3449
     * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode
3450
     */
3451 2
    public function CustomFunctionsReturningNumerics()
3452
    {
3453
        // getCustomNumericFunction is case-insensitive
3454 2
        $functionName  = strtolower($this->lexer->lookahead['value']);
3455 2
        $functionClass = $this->em->getConfiguration()->getCustomNumericFunction($functionName);
3456
3457 2
        $function = is_string($functionClass)
3458 1
            ? new $functionClass($functionName)
3459 2
            : call_user_func($functionClass, $functionName);
3460
3461 2
        $function->parse($this);
3462
3463 2
        return $function;
3464
    }
3465
3466
    /**
3467
     * FunctionsReturningDateTime ::=
3468
     *     "CURRENT_DATE" |
3469
     *     "CURRENT_TIME" |
3470
     *     "CURRENT_TIMESTAMP" |
3471
     *     "DATE_ADD" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" |
3472
     *     "DATE_SUB" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")"
3473
     *
3474
     * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode
3475
     */
3476 7
    public function FunctionsReturningDatetime()
3477
    {
3478 7
        $funcNameLower = strtolower($this->lexer->lookahead['value']);
3479 7
        $funcClass     = self::$_DATETIME_FUNCTIONS[$funcNameLower];
3480
3481 7
        $function = new $funcClass($funcNameLower);
3482 7
        $function->parse($this);
3483
3484 7
        return $function;
3485
    }
3486
3487
    /**
3488
     * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode
3489
     */
3490
    public function CustomFunctionsReturningDatetime()
3491
    {
3492
        // getCustomDatetimeFunction is case-insensitive
3493
        $functionName  = $this->lexer->lookahead['value'];
3494
        $functionClass = $this->em->getConfiguration()->getCustomDatetimeFunction($functionName);
3495
3496
        $function = is_string($functionClass)
3497
            ? new $functionClass($functionName)
3498
            : call_user_func($functionClass, $functionName);
3499
3500
        $function->parse($this);
3501
3502
        return $function;
3503
    }
3504
3505
    /**
3506
     * FunctionsReturningStrings ::=
3507
     *   "CONCAT" "(" StringPrimary "," StringPrimary {"," StringPrimary}* ")" |
3508
     *   "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" |
3509
     *   "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" |
3510
     *   "LOWER" "(" StringPrimary ")" |
3511
     *   "UPPER" "(" StringPrimary ")" |
3512
     *   "IDENTITY" "(" SingleValuedAssociationPathExpression {"," string} ")"
3513
     *
3514
     * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode
3515
     */
3516 30
    public function FunctionsReturningStrings()
3517
    {
3518 30
        $funcNameLower = strtolower($this->lexer->lookahead['value']);
3519 30
        $funcClass     = self::$_STRING_FUNCTIONS[$funcNameLower];
3520
3521 30
        $function = new $funcClass($funcNameLower);
3522 30
        $function->parse($this);
3523
3524 30
        return $function;
3525
    }
3526
3527
    /**
3528
     * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode
3529
     */
3530 2
    public function CustomFunctionsReturningStrings()
3531
    {
3532
        // getCustomStringFunction is case-insensitive
3533 2
        $functionName  = $this->lexer->lookahead['value'];
3534 2
        $functionClass = $this->em->getConfiguration()->getCustomStringFunction($functionName);
3535
3536 2
        $function = is_string($functionClass)
3537 1
            ? new $functionClass($functionName)
3538 2
            : call_user_func($functionClass, $functionName);
3539
3540 2
        $function->parse($this);
3541
3542 2
        return $function;
3543
    }
3544
}
3545