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

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