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

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