vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php line 411

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