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

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