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

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