Complex classes like Parser often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Parser, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 37 | class Parser |
||
| 38 | { |
||
| 39 | /** |
||
| 40 | * READ-ONLY: Maps BUILT-IN string function names to AST class names. |
||
| 41 | * |
||
| 42 | * @var array |
||
| 43 | */ |
||
| 44 | private static $_STRING_FUNCTIONS = [ |
||
| 45 | 'concat' => Functions\ConcatFunction::class, |
||
| 46 | 'substring' => Functions\SubstringFunction::class, |
||
| 47 | 'trim' => Functions\TrimFunction::class, |
||
| 48 | 'lower' => Functions\LowerFunction::class, |
||
| 49 | 'upper' => Functions\UpperFunction::class, |
||
| 50 | 'identity' => Functions\IdentityFunction::class, |
||
| 51 | ]; |
||
| 52 | |||
| 53 | /** |
||
| 54 | * READ-ONLY: Maps BUILT-IN numeric function names to AST class names. |
||
| 55 | * |
||
| 56 | * @var array |
||
| 57 | */ |
||
| 58 | private static $_NUMERIC_FUNCTIONS = [ |
||
| 59 | 'length' => Functions\LengthFunction::class, |
||
| 60 | 'locate' => Functions\LocateFunction::class, |
||
| 61 | 'abs' => Functions\AbsFunction::class, |
||
| 62 | 'sqrt' => Functions\SqrtFunction::class, |
||
| 63 | 'mod' => Functions\ModFunction::class, |
||
| 64 | 'size' => Functions\SizeFunction::class, |
||
| 65 | 'date_diff' => Functions\DateDiffFunction::class, |
||
| 66 | 'bit_and' => Functions\BitAndFunction::class, |
||
| 67 | 'bit_or' => Functions\BitOrFunction::class, |
||
| 68 | |||
| 69 | // Aggregate functions |
||
| 70 | 'min' => Functions\MinFunction::class, |
||
| 71 | 'max' => Functions\MaxFunction::class, |
||
| 72 | 'avg' => Functions\AvgFunction::class, |
||
| 73 | 'sum' => Functions\SumFunction::class, |
||
| 74 | 'count' => Functions\CountFunction::class, |
||
| 75 | ]; |
||
| 76 | |||
| 77 | /** |
||
| 78 | * READ-ONLY: Maps BUILT-IN datetime function names to AST class names. |
||
| 79 | * |
||
| 80 | * @var array |
||
| 81 | */ |
||
| 82 | private static $_DATETIME_FUNCTIONS = [ |
||
| 83 | 'current_date' => Functions\CurrentDateFunction::class, |
||
| 84 | 'current_time' => Functions\CurrentTimeFunction::class, |
||
| 85 | 'current_timestamp' => Functions\CurrentTimestampFunction::class, |
||
| 86 | 'date_add' => Functions\DateAddFunction::class, |
||
| 87 | 'date_sub' => Functions\DateSubFunction::class, |
||
| 88 | ]; |
||
| 89 | |||
| 90 | /* |
||
| 91 | * Expressions that were encountered during parsing of identifiers and expressions |
||
| 92 | * and still need to be validated. |
||
| 93 | */ |
||
| 94 | |||
| 95 | /** |
||
| 96 | * @var array |
||
| 97 | */ |
||
| 98 | private $deferredIdentificationVariables = []; |
||
| 99 | |||
| 100 | /** |
||
| 101 | * @var array |
||
| 102 | */ |
||
| 103 | private $deferredPartialObjectExpressions = []; |
||
| 104 | |||
| 105 | /** |
||
| 106 | * @var array |
||
| 107 | */ |
||
| 108 | private $deferredPathExpressions = []; |
||
| 109 | |||
| 110 | /** |
||
| 111 | * @var array |
||
| 112 | */ |
||
| 113 | private $deferredResultVariables = []; |
||
| 114 | |||
| 115 | /** |
||
| 116 | * @var array |
||
| 117 | */ |
||
| 118 | private $deferredNewObjectExpressions = []; |
||
| 119 | |||
| 120 | /** |
||
| 121 | * The lexer. |
||
| 122 | * |
||
| 123 | * @var \Doctrine\ORM\Query\Lexer |
||
| 124 | */ |
||
| 125 | private $lexer; |
||
| 126 | |||
| 127 | /** |
||
| 128 | * The parser result. |
||
| 129 | * |
||
| 130 | * @var \Doctrine\ORM\Query\ParserResult |
||
| 131 | */ |
||
| 132 | private $parserResult; |
||
| 133 | |||
| 134 | /** |
||
| 135 | * The EntityManager. |
||
| 136 | * |
||
| 137 | * @var \Doctrine\ORM\EntityManager |
||
| 138 | */ |
||
| 139 | private $em; |
||
| 140 | |||
| 141 | /** |
||
| 142 | * The Query to parse. |
||
| 143 | * |
||
| 144 | * @var Query |
||
| 145 | */ |
||
| 146 | private $query; |
||
| 147 | |||
| 148 | /** |
||
| 149 | * Map of declared query components in the parsed query. |
||
| 150 | * |
||
| 151 | * @var array |
||
| 152 | */ |
||
| 153 | private $queryComponents = []; |
||
| 154 | |||
| 155 | /** |
||
| 156 | * Keeps the nesting level of defined ResultVariables. |
||
| 157 | * |
||
| 158 | * @var integer |
||
| 159 | */ |
||
| 160 | private $nestingLevel = 0; |
||
| 161 | |||
| 162 | /** |
||
| 163 | * Any additional custom tree walkers that modify the AST. |
||
| 164 | * |
||
| 165 | * @var array |
||
| 166 | */ |
||
| 167 | private $customTreeWalkers = []; |
||
| 168 | |||
| 169 | /** |
||
| 170 | * The custom last tree walker, if any, that is responsible for producing the output. |
||
| 171 | * |
||
| 172 | * @var TreeWalker |
||
| 173 | */ |
||
| 174 | private $customOutputWalker; |
||
| 175 | |||
| 176 | /** |
||
| 177 | * @var array |
||
| 178 | */ |
||
| 179 | private $identVariableExpressions = []; |
||
| 180 | |||
| 181 | /** |
||
| 182 | * Creates a new query parser object. |
||
| 183 | * |
||
| 184 | * @param Query $query The Query to parse. |
||
| 185 | */ |
||
| 186 | 757 | public function __construct(Query $query) |
|
| 193 | |||
| 194 | /** |
||
| 195 | * Sets a custom tree walker that produces output. |
||
| 196 | * This tree walker will be run last over the AST, after any other walkers. |
||
| 197 | * |
||
| 198 | * @param string $className |
||
| 199 | * |
||
| 200 | * @return void |
||
| 201 | */ |
||
| 202 | 111 | public function setCustomOutputTreeWalker($className) |
|
| 206 | |||
| 207 | /** |
||
| 208 | * Adds a custom tree walker for modifying the AST. |
||
| 209 | * |
||
| 210 | * @param string $className |
||
| 211 | * |
||
| 212 | * @return void |
||
| 213 | */ |
||
| 214 | public function addCustomTreeWalker($className) |
||
| 218 | |||
| 219 | /** |
||
| 220 | * Gets the lexer used by the parser. |
||
| 221 | * |
||
| 222 | * @return \Doctrine\ORM\Query\Lexer |
||
| 223 | */ |
||
| 224 | 27 | public function getLexer() |
|
| 228 | |||
| 229 | /** |
||
| 230 | * Gets the ParserResult that is being filled with information during parsing. |
||
| 231 | * |
||
| 232 | * @return \Doctrine\ORM\Query\ParserResult |
||
| 233 | */ |
||
| 234 | public function getParserResult() |
||
| 238 | |||
| 239 | /** |
||
| 240 | * Gets the EntityManager used by the parser. |
||
| 241 | * |
||
| 242 | * @return \Doctrine\ORM\EntityManager |
||
| 243 | */ |
||
| 244 | public function getEntityManager() |
||
| 248 | |||
| 249 | /** |
||
| 250 | * Parses and builds AST for the given Query. |
||
| 251 | * |
||
| 252 | * @return \Doctrine\ORM\Query\AST\SelectStatement | |
||
| 253 | * \Doctrine\ORM\Query\AST\UpdateStatement | |
||
| 254 | * \Doctrine\ORM\Query\AST\DeleteStatement |
||
| 255 | */ |
||
| 256 | 757 | public function getAST() |
|
| 288 | |||
| 289 | /** |
||
| 290 | * Attempts to match the given token with the current lookahead token. |
||
| 291 | * |
||
| 292 | * If they match, updates the lookahead token; otherwise raises a syntax |
||
| 293 | * error. |
||
| 294 | * |
||
| 295 | * @param int $token The token type. |
||
| 296 | * |
||
| 297 | * @return void |
||
| 298 | * |
||
| 299 | * @throws QueryException If the tokens don't match. |
||
| 300 | */ |
||
| 301 | 768 | public function match($token) |
|
| 325 | |||
| 326 | /** |
||
| 327 | * Frees this parser, enabling it to be reused. |
||
| 328 | * |
||
| 329 | * @param boolean $deep Whether to clean peek and reset errors. |
||
| 330 | * @param integer $position Position to reset. |
||
| 331 | * |
||
| 332 | * @return void |
||
| 333 | */ |
||
| 334 | public function free($deep = false, $position = 0) |
||
| 347 | |||
| 348 | /** |
||
| 349 | * Parses a query string. |
||
| 350 | * |
||
| 351 | * @return ParserResult |
||
| 352 | */ |
||
| 353 | 757 | public function parse() |
|
| 398 | |||
| 399 | /** |
||
| 400 | * Fixes order of identification variables. |
||
| 401 | * |
||
| 402 | * They have to appear in the select clause in the same order as the |
||
| 403 | * declarations (from ... x join ... y join ... z ...) appear in the query |
||
| 404 | * as the hydration process relies on that order for proper operation. |
||
| 405 | * |
||
| 406 | * @param AST\SelectStatement|AST\DeleteStatement|AST\UpdateStatement $AST |
||
| 407 | * |
||
| 408 | * @return void |
||
| 409 | */ |
||
| 410 | 697 | private function fixIdentificationVariableOrder($AST) |
|
| 429 | |||
| 430 | /** |
||
| 431 | * Generates a new syntax error. |
||
| 432 | * |
||
| 433 | * @param string $expected Expected string. |
||
| 434 | * @param array|null $token Got token. |
||
| 435 | * |
||
| 436 | * @return void |
||
| 437 | * |
||
| 438 | * @throws \Doctrine\ORM\Query\QueryException |
||
| 439 | */ |
||
| 440 | 18 | public function syntaxError($expected = '', $token = null) |
|
| 454 | |||
| 455 | /** |
||
| 456 | * Generates a new semantical error. |
||
| 457 | * |
||
| 458 | * @param string $message Optional message. |
||
| 459 | * @param array|null $token Optional token. |
||
| 460 | * |
||
| 461 | * @return void |
||
| 462 | * |
||
| 463 | * @throws \Doctrine\ORM\Query\QueryException |
||
| 464 | */ |
||
| 465 | 29 | public function semanticalError($message = '', $token = null) |
|
| 489 | |||
| 490 | /** |
||
| 491 | * Peeks beyond the matched closing parenthesis and returns the first token after that one. |
||
| 492 | * |
||
| 493 | * @param boolean $resetPeek Reset peek after finding the closing parenthesis. |
||
| 494 | * |
||
| 495 | * @return array |
||
| 496 | */ |
||
| 497 | 91 | private function peekBeyondClosingParenthesis($resetPeek = true) |
|
| 525 | |||
| 526 | /** |
||
| 527 | * Checks if the given token indicates a mathematical operator. |
||
| 528 | * |
||
| 529 | * @param array $token |
||
| 530 | * |
||
| 531 | * @return boolean TRUE if the token is a mathematical operator, FALSE otherwise. |
||
| 532 | */ |
||
| 533 | 277 | private function isMathOperator($token) |
|
| 537 | |||
| 538 | /** |
||
| 539 | * Checks if the next-next (after lookahead) token starts a function. |
||
| 540 | * |
||
| 541 | * @return boolean TRUE if the next-next tokens start a function, FALSE otherwise. |
||
| 542 | */ |
||
| 543 | 319 | private function isFunction() |
|
| 552 | |||
| 553 | /** |
||
| 554 | * Checks whether the given token type indicates an aggregate function. |
||
| 555 | * |
||
| 556 | * @param int $tokenType |
||
| 557 | * |
||
| 558 | * @return boolean TRUE if the token type is an aggregate function, FALSE otherwise. |
||
| 559 | */ |
||
| 560 | 1 | private function isAggregateFunction($tokenType) |
|
| 564 | |||
| 565 | /** |
||
| 566 | * Checks whether the current lookahead token of the lexer has the type T_ALL, T_ANY or T_SOME. |
||
| 567 | * |
||
| 568 | * @return boolean |
||
| 569 | */ |
||
| 570 | 259 | private function isNextAllAnySome() |
|
| 574 | |||
| 575 | /** |
||
| 576 | * Validates that the given <tt>IdentificationVariable</tt> is semantically correct. |
||
| 577 | * It must exist in query components list. |
||
| 578 | * |
||
| 579 | * @return void |
||
| 580 | */ |
||
| 581 | 708 | private function processDeferredIdentificationVariables() |
|
| 582 | { |
||
| 583 | 708 | foreach ($this->deferredIdentificationVariables as $deferredItem) { |
|
| 584 | 696 | $identVariable = $deferredItem['expression']; |
|
| 585 | |||
| 586 | // Check if IdentificationVariable exists in queryComponents |
||
| 587 | 696 | if ( ! isset($this->queryComponents[$identVariable])) { |
|
| 588 | 1 | $this->semanticalError( |
|
| 589 | 1 | "'$identVariable' is not defined.", $deferredItem['token'] |
|
| 590 | ); |
||
| 591 | } |
||
| 592 | |||
| 593 | 696 | $qComp = $this->queryComponents[$identVariable]; |
|
| 594 | |||
| 595 | // Check if queryComponent points to an AbstractSchemaName or a ResultVariable |
||
| 596 | 696 | if ( ! isset($qComp['metadata'])) { |
|
| 597 | $this->semanticalError( |
||
| 598 | "'$identVariable' does not point to a Class.", $deferredItem['token'] |
||
| 599 | ); |
||
| 600 | } |
||
| 601 | |||
| 602 | // Validate if identification variable nesting level is lower or equal than the current one |
||
| 603 | 696 | if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) { |
|
| 604 | $this->semanticalError( |
||
| 605 | 696 | "'$identVariable' is used outside the scope of its declaration.", $deferredItem['token'] |
|
| 606 | ); |
||
| 607 | } |
||
| 608 | } |
||
| 609 | 707 | } |
|
| 610 | |||
| 611 | /** |
||
| 612 | * Validates that the given <tt>NewObjectExpression</tt>. |
||
| 613 | * |
||
| 614 | * @param \Doctrine\ORM\Query\AST\SelectClause $AST |
||
| 615 | * |
||
| 616 | * @return void |
||
| 617 | */ |
||
| 618 | 26 | private function processDeferredNewObjectExpressions($AST) |
|
| 659 | |||
| 660 | /** |
||
| 661 | * Validates that the given <tt>PartialObjectExpression</tt> is semantically correct. |
||
| 662 | * It must exist in query components list. |
||
| 663 | * |
||
| 664 | * @return void |
||
| 665 | */ |
||
| 666 | 11 | private function processDeferredPartialObjectExpressions() |
|
| 696 | |||
| 697 | /** |
||
| 698 | * Validates that the given <tt>ResultVariable</tt> is semantically correct. |
||
| 699 | * It must exist in query components list. |
||
| 700 | * |
||
| 701 | * @return void |
||
| 702 | */ |
||
| 703 | 9 | private function processDeferredResultVariables() |
|
| 732 | |||
| 733 | /** |
||
| 734 | * Validates that the given <tt>PathExpression</tt> is semantically correct for grammar rules: |
||
| 735 | * |
||
| 736 | * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression |
||
| 737 | * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression |
||
| 738 | * StateFieldPathExpression ::= IdentificationVariable "." StateField |
||
| 739 | * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField |
||
| 740 | * CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField |
||
| 741 | * |
||
| 742 | * @return void |
||
| 743 | */ |
||
| 744 | 511 | private function processDeferredPathExpressions() |
|
| 809 | |||
| 810 | /** |
||
| 811 | * @return void |
||
| 812 | */ |
||
| 813 | 698 | private function processRootEntityAliasSelected() |
|
| 827 | |||
| 828 | /** |
||
| 829 | * QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement |
||
| 830 | * |
||
| 831 | * @return \Doctrine\ORM\Query\AST\SelectStatement | |
||
| 832 | * \Doctrine\ORM\Query\AST\UpdateStatement | |
||
| 833 | * \Doctrine\ORM\Query\AST\DeleteStatement |
||
| 834 | */ |
||
| 835 | 757 | public function QueryLanguage() |
|
| 864 | |||
| 865 | /** |
||
| 866 | * SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] |
||
| 867 | * |
||
| 868 | * @return \Doctrine\ORM\Query\AST\SelectStatement |
||
| 869 | */ |
||
| 870 | 692 | public function SelectStatement() |
|
| 881 | |||
| 882 | /** |
||
| 883 | * UpdateStatement ::= UpdateClause [WhereClause] |
||
| 884 | * |
||
| 885 | * @return \Doctrine\ORM\Query\AST\UpdateStatement |
||
| 886 | */ |
||
| 887 | 31 | public function UpdateStatement() |
|
| 895 | |||
| 896 | /** |
||
| 897 | * DeleteStatement ::= DeleteClause [WhereClause] |
||
| 898 | * |
||
| 899 | * @return \Doctrine\ORM\Query\AST\DeleteStatement |
||
| 900 | */ |
||
| 901 | 40 | public function DeleteStatement() |
|
| 909 | |||
| 910 | /** |
||
| 911 | * IdentificationVariable ::= identifier |
||
| 912 | * |
||
| 913 | * @return string |
||
| 914 | */ |
||
| 915 | 734 | public function IdentificationVariable() |
|
| 929 | |||
| 930 | /** |
||
| 931 | * AliasIdentificationVariable = identifier |
||
| 932 | * |
||
| 933 | * @return string |
||
| 934 | */ |
||
| 935 | 717 | public function AliasIdentificationVariable() |
|
| 948 | |||
| 949 | /** |
||
| 950 | * AbstractSchemaName ::= fully_qualified_name | aliased_name | identifier |
||
| 951 | * |
||
| 952 | * @return string |
||
| 953 | */ |
||
| 954 | 738 | public function AbstractSchemaName() |
|
| 974 | |||
| 975 | /** |
||
| 976 | * Validates an AbstractSchemaName, making sure the class exists. |
||
| 977 | * |
||
| 978 | * @param string $schemaName The name to validate. |
||
| 979 | * |
||
| 980 | * @throws QueryException if the name does not exist. |
||
| 981 | */ |
||
| 982 | 732 | private function validateAbstractSchemaName($schemaName) |
|
| 988 | |||
| 989 | /** |
||
| 990 | * AliasResultVariable ::= identifier |
||
| 991 | * |
||
| 992 | * @return string |
||
| 993 | */ |
||
| 994 | 68 | public function AliasResultVariable() |
|
| 995 | { |
||
| 996 | 68 | $this->match(Lexer::T_IDENTIFIER); |
|
| 997 | |||
| 998 | 64 | $resultVariable = $this->lexer->token['value']; |
|
| 999 | 64 | $exists = isset($this->queryComponents[$resultVariable]); |
|
| 1000 | |||
| 1001 | 64 | if ($exists) { |
|
| 1002 | $this->semanticalError("'$resultVariable' is already defined.", $this->lexer->token); |
||
| 1003 | } |
||
| 1004 | |||
| 1005 | 64 | return $resultVariable; |
|
| 1006 | } |
||
| 1007 | |||
| 1008 | /** |
||
| 1009 | * ResultVariable ::= identifier |
||
| 1010 | * |
||
| 1011 | * @return string |
||
| 1012 | */ |
||
| 1013 | 9 | public function ResultVariable() |
|
| 1028 | |||
| 1029 | /** |
||
| 1030 | * JoinAssociationPathExpression ::= IdentificationVariable "." (CollectionValuedAssociationField | SingleValuedAssociationField) |
||
| 1031 | * |
||
| 1032 | * @return \Doctrine\ORM\Query\AST\JoinAssociationPathExpression |
||
| 1033 | */ |
||
| 1034 | 228 | public function JoinAssociationPathExpression() |
|
| 1059 | |||
| 1060 | /** |
||
| 1061 | * Parses an arbitrary path expression and defers semantical validation |
||
| 1062 | * based on expected types. |
||
| 1063 | * |
||
| 1064 | * PathExpression ::= IdentificationVariable {"." identifier}* |
||
| 1065 | * |
||
| 1066 | * @param integer $expectedTypes |
||
| 1067 | * |
||
| 1068 | * @return \Doctrine\ORM\Query\AST\PathExpression |
||
| 1069 | */ |
||
| 1070 | 529 | public function PathExpression($expectedTypes) |
|
| 1100 | |||
| 1101 | /** |
||
| 1102 | * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression |
||
| 1103 | * |
||
| 1104 | * @return \Doctrine\ORM\Query\AST\PathExpression |
||
| 1105 | */ |
||
| 1106 | public function AssociationPathExpression() |
||
| 1113 | |||
| 1114 | /** |
||
| 1115 | * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression |
||
| 1116 | * |
||
| 1117 | * @return \Doctrine\ORM\Query\AST\PathExpression |
||
| 1118 | */ |
||
| 1119 | 441 | public function SingleValuedPathExpression() |
|
| 1126 | |||
| 1127 | /** |
||
| 1128 | * StateFieldPathExpression ::= IdentificationVariable "." StateField |
||
| 1129 | * |
||
| 1130 | * @return \Doctrine\ORM\Query\AST\PathExpression |
||
| 1131 | */ |
||
| 1132 | 177 | public function StateFieldPathExpression() |
|
| 1136 | |||
| 1137 | /** |
||
| 1138 | * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField |
||
| 1139 | * |
||
| 1140 | * @return \Doctrine\ORM\Query\AST\PathExpression |
||
| 1141 | */ |
||
| 1142 | 9 | public function SingleValuedAssociationPathExpression() |
|
| 1146 | |||
| 1147 | /** |
||
| 1148 | * CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField |
||
| 1149 | * |
||
| 1150 | * @return \Doctrine\ORM\Query\AST\PathExpression |
||
| 1151 | */ |
||
| 1152 | 21 | public function CollectionValuedPathExpression() |
|
| 1156 | |||
| 1157 | /** |
||
| 1158 | * SelectClause ::= "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression} |
||
| 1159 | * |
||
| 1160 | * @return \Doctrine\ORM\Query\AST\SelectClause |
||
| 1161 | */ |
||
| 1162 | 692 | public function SelectClause() |
|
| 1186 | |||
| 1187 | /** |
||
| 1188 | * SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression |
||
| 1189 | * |
||
| 1190 | * @return \Doctrine\ORM\Query\AST\SimpleSelectClause |
||
| 1191 | */ |
||
| 1192 | 27 | public function SimpleSelectClause() |
|
| 1205 | |||
| 1206 | /** |
||
| 1207 | * UpdateClause ::= "UPDATE" AbstractSchemaName ["AS"] AliasIdentificationVariable "SET" UpdateItem {"," UpdateItem}* |
||
| 1208 | * |
||
| 1209 | * @return \Doctrine\ORM\Query\AST\UpdateClause |
||
| 1210 | */ |
||
| 1211 | 31 | public function UpdateClause() |
|
| 1256 | |||
| 1257 | /** |
||
| 1258 | * DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName ["AS"] AliasIdentificationVariable |
||
| 1259 | * |
||
| 1260 | * @return \Doctrine\ORM\Query\AST\DeleteClause |
||
| 1261 | */ |
||
| 1262 | 40 | public function DeleteClause() |
|
| 1263 | { |
||
| 1264 | 40 | $this->match(Lexer::T_DELETE); |
|
| 1265 | |||
| 1266 | 40 | if ($this->lexer->isNextToken(Lexer::T_FROM)) { |
|
| 1267 | 8 | $this->match(Lexer::T_FROM); |
|
| 1268 | } |
||
| 1269 | |||
| 1270 | 40 | $token = $this->lexer->lookahead; |
|
| 1271 | 40 | $abstractSchemaName = $this->AbstractSchemaName(); |
|
| 1272 | |||
| 1273 | 40 | $this->validateAbstractSchemaName($abstractSchemaName); |
|
| 1274 | |||
| 1275 | 40 | $deleteClause = new AST\DeleteClause($abstractSchemaName); |
|
| 1276 | |||
| 1277 | 40 | if ($this->lexer->isNextToken(Lexer::T_AS)) { |
|
| 1278 | $this->match(Lexer::T_AS); |
||
| 1279 | } |
||
| 1280 | |||
| 1281 | 40 | $aliasIdentificationVariable = $this->AliasIdentificationVariable(); |
|
| 1282 | |||
| 1283 | 39 | $deleteClause->aliasIdentificationVariable = $aliasIdentificationVariable; |
|
| 1284 | 39 | $class = $this->em->getClassMetadata($deleteClause->abstractSchemaName); |
|
| 1285 | |||
| 1286 | // Building queryComponent |
||
| 1287 | $queryComponent = [ |
||
| 1288 | 39 | 'metadata' => $class, |
|
| 1289 | 'parent' => null, |
||
| 1290 | 'relation' => null, |
||
| 1291 | 'map' => null, |
||
| 1292 | 39 | 'nestingLevel' => $this->nestingLevel, |
|
| 1293 | 39 | 'token' => $token, |
|
| 1294 | ]; |
||
| 1295 | |||
| 1296 | 39 | $this->queryComponents[$aliasIdentificationVariable] = $queryComponent; |
|
| 1297 | |||
| 1298 | 39 | return $deleteClause; |
|
| 1299 | } |
||
| 1300 | |||
| 1301 | /** |
||
| 1302 | * FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}* |
||
| 1303 | * |
||
| 1304 | * @return \Doctrine\ORM\Query\AST\FromClause |
||
| 1305 | */ |
||
| 1306 | 674 | public function FromClause() |
|
| 1321 | |||
| 1322 | /** |
||
| 1323 | * SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}* |
||
| 1324 | * |
||
| 1325 | * @return \Doctrine\ORM\Query\AST\SubselectFromClause |
||
| 1326 | */ |
||
| 1327 | 26 | public function SubselectFromClause() |
|
| 1342 | |||
| 1343 | /** |
||
| 1344 | * WhereClause ::= "WHERE" ConditionalExpression |
||
| 1345 | * |
||
| 1346 | * @return \Doctrine\ORM\Query\AST\WhereClause |
||
| 1347 | */ |
||
| 1348 | 313 | public function WhereClause() |
|
| 1354 | |||
| 1355 | /** |
||
| 1356 | * HavingClause ::= "HAVING" ConditionalExpression |
||
| 1357 | * |
||
| 1358 | * @return \Doctrine\ORM\Query\AST\HavingClause |
||
| 1359 | */ |
||
| 1360 | 4 | public function HavingClause() |
|
| 1366 | |||
| 1367 | /** |
||
| 1368 | * GroupByClause ::= "GROUP" "BY" GroupByItem {"," GroupByItem}* |
||
| 1369 | * |
||
| 1370 | * @return \Doctrine\ORM\Query\AST\GroupByClause |
||
| 1371 | */ |
||
| 1372 | 7 | public function GroupByClause() |
|
| 1387 | |||
| 1388 | /** |
||
| 1389 | * OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}* |
||
| 1390 | * |
||
| 1391 | * @return \Doctrine\ORM\Query\AST\OrderByClause |
||
| 1392 | */ |
||
| 1393 | 156 | public function OrderByClause() |
|
| 1409 | |||
| 1410 | /** |
||
| 1411 | * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] |
||
| 1412 | * |
||
| 1413 | * @return \Doctrine\ORM\Query\AST\Subselect |
||
| 1414 | */ |
||
| 1415 | 27 | public function Subselect() |
|
| 1432 | |||
| 1433 | /** |
||
| 1434 | * UpdateItem ::= SingleValuedPathExpression "=" NewValue |
||
| 1435 | * |
||
| 1436 | * @return \Doctrine\ORM\Query\AST\UpdateItem |
||
| 1437 | */ |
||
| 1438 | 31 | public function UpdateItem() |
|
| 1448 | |||
| 1449 | /** |
||
| 1450 | * GroupByItem ::= IdentificationVariable | ResultVariable | SingleValuedPathExpression |
||
| 1451 | * |
||
| 1452 | * @return string | \Doctrine\ORM\Query\AST\PathExpression |
||
| 1453 | */ |
||
| 1454 | 7 | public function GroupByItem() |
|
| 1455 | { |
||
| 1456 | // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression |
||
| 1457 | 7 | $glimpse = $this->lexer->glimpse(); |
|
| 1458 | |||
| 1459 | 7 | if ($glimpse['type'] === Lexer::T_DOT) { |
|
| 1460 | 1 | return $this->SingleValuedPathExpression(); |
|
| 1461 | } |
||
| 1462 | |||
| 1463 | // Still need to decide between IdentificationVariable or ResultVariable |
||
| 1464 | 6 | $lookaheadValue = $this->lexer->lookahead['value']; |
|
| 1465 | |||
| 1466 | 6 | if ( ! isset($this->queryComponents[$lookaheadValue])) { |
|
| 1467 | $this->semanticalError('Cannot group by undefined identification or result variable.'); |
||
| 1468 | } |
||
| 1469 | |||
| 1470 | 6 | return (isset($this->queryComponents[$lookaheadValue]['metadata'])) |
|
| 1471 | 4 | ? $this->IdentificationVariable() |
|
| 1472 | 6 | : $this->ResultVariable(); |
|
| 1473 | } |
||
| 1474 | |||
| 1475 | /** |
||
| 1476 | * OrderByItem ::= ( |
||
| 1477 | * SimpleArithmeticExpression | SingleValuedPathExpression | |
||
| 1478 | * ScalarExpression | ResultVariable | FunctionDeclaration |
||
| 1479 | * ) ["ASC" | "DESC"] |
||
| 1480 | * |
||
| 1481 | * @return \Doctrine\ORM\Query\AST\OrderByItem |
||
| 1482 | */ |
||
| 1483 | 156 | public function OrderByItem() |
|
| 1537 | |||
| 1538 | /** |
||
| 1539 | * NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary | |
||
| 1540 | * EnumPrimary | SimpleEntityExpression | "NULL" |
||
| 1541 | * |
||
| 1542 | * NOTE: Since it is not possible to correctly recognize individual types, here is the full |
||
| 1543 | * grammar that needs to be supported: |
||
| 1544 | * |
||
| 1545 | * NewValue ::= SimpleArithmeticExpression | "NULL" |
||
| 1546 | * |
||
| 1547 | * SimpleArithmeticExpression covers all *Primary grammar rules and also SimpleEntityExpression |
||
| 1548 | * |
||
| 1549 | * @return AST\ArithmeticExpression |
||
| 1550 | */ |
||
| 1551 | 31 | public function NewValue() |
|
| 1567 | |||
| 1568 | /** |
||
| 1569 | * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {Join}* |
||
| 1570 | * |
||
| 1571 | * @return \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration |
||
| 1572 | */ |
||
| 1573 | 670 | public function IdentificationVariableDeclaration() |
|
| 1595 | |||
| 1596 | /** |
||
| 1597 | * SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration |
||
| 1598 | * |
||
| 1599 | * {Internal note: WARNING: Solution is harder than a bare implementation. |
||
| 1600 | * Desired EBNF support: |
||
| 1601 | * |
||
| 1602 | * SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration | (AssociationPathExpression ["AS"] AliasIdentificationVariable) |
||
| 1603 | * |
||
| 1604 | * It demands that entire SQL generation to become programmatical. This is |
||
| 1605 | * needed because association based subselect requires "WHERE" conditional |
||
| 1606 | * expressions to be injected, but there is no scope to do that. Only scope |
||
| 1607 | * accessible is "FROM", prohibiting an easy implementation without larger |
||
| 1608 | * changes.} |
||
| 1609 | * |
||
| 1610 | * @return \Doctrine\ORM\Query\AST\SubselectIdentificationVariableDeclaration | |
||
| 1611 | * \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration |
||
| 1612 | */ |
||
| 1613 | 26 | public function SubselectIdentificationVariableDeclaration() |
|
| 1654 | |||
| 1655 | /** |
||
| 1656 | * Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" |
||
| 1657 | * (JoinAssociationDeclaration | RangeVariableDeclaration) |
||
| 1658 | * ["WITH" ConditionalExpression] |
||
| 1659 | * |
||
| 1660 | * @return \Doctrine\ORM\Query\AST\Join |
||
| 1661 | */ |
||
| 1662 | 247 | public function Join() |
|
| 1710 | |||
| 1711 | /** |
||
| 1712 | * RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable |
||
| 1713 | * |
||
| 1714 | * @return \Doctrine\ORM\Query\AST\RangeVariableDeclaration |
||
| 1715 | */ |
||
| 1716 | 670 | public function RangeVariableDeclaration() |
|
| 1744 | |||
| 1745 | /** |
||
| 1746 | * JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable [IndexBy] |
||
| 1747 | * |
||
| 1748 | * @return \Doctrine\ORM\Query\AST\JoinAssociationPathExpression |
||
| 1749 | */ |
||
| 1750 | 228 | public function JoinAssociationDeclaration() |
|
| 1781 | |||
| 1782 | /** |
||
| 1783 | * PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet |
||
| 1784 | * PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}" |
||
| 1785 | * |
||
| 1786 | * @return \Doctrine\ORM\Query\AST\PartialObjectExpression |
||
| 1787 | */ |
||
| 1788 | 11 | public function PartialObjectExpression() |
|
| 1839 | |||
| 1840 | /** |
||
| 1841 | * NewObjectExpression ::= "NEW" AbstractSchemaName "(" NewObjectArg {"," NewObjectArg}* ")" |
||
| 1842 | * |
||
| 1843 | * @return \Doctrine\ORM\Query\AST\NewObjectExpression |
||
| 1844 | */ |
||
| 1845 | 26 | public function NewObjectExpression() |
|
| 1875 | |||
| 1876 | /** |
||
| 1877 | * NewObjectArg ::= ScalarExpression | "(" Subselect ")" |
||
| 1878 | * |
||
| 1879 | * @return mixed |
||
| 1880 | */ |
||
| 1881 | 26 | public function NewObjectArg() |
|
| 1896 | |||
| 1897 | /** |
||
| 1898 | * IndexBy ::= "INDEX" "BY" StateFieldPathExpression |
||
| 1899 | * |
||
| 1900 | * @return \Doctrine\ORM\Query\AST\IndexBy |
||
| 1901 | */ |
||
| 1902 | 11 | public function IndexBy() |
|
| 1913 | |||
| 1914 | /** |
||
| 1915 | * ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary | |
||
| 1916 | * StateFieldPathExpression | BooleanPrimary | CaseExpression | |
||
| 1917 | * InstanceOfExpression |
||
| 1918 | * |
||
| 1919 | * @return mixed One of the possible expressions or subexpressions. |
||
| 1920 | */ |
||
| 1921 | 129 | public function ScalarExpression() |
|
| 1922 | { |
||
| 1923 | 129 | $lookahead = $this->lexer->lookahead['type']; |
|
| 1924 | 129 | $peek = $this->lexer->glimpse(); |
|
| 1925 | |||
| 1926 | switch (true) { |
||
| 1927 | 129 | case ($lookahead === Lexer::T_INTEGER): |
|
| 1928 | 126 | case ($lookahead === Lexer::T_FLOAT): |
|
| 1929 | // SimpleArithmeticExpression : (- u.value ) or ( + u.value ) or ( - 1 ) or ( + 1 ) |
||
| 1930 | 126 | case ($lookahead === Lexer::T_MINUS): |
|
| 1931 | 126 | case ($lookahead === Lexer::T_PLUS): |
|
| 1932 | 15 | return $this->SimpleArithmeticExpression(); |
|
| 1933 | |||
| 1934 | 126 | case ($lookahead === Lexer::T_STRING): |
|
| 1935 | 13 | return $this->StringPrimary(); |
|
| 1936 | |||
| 1937 | 124 | case ($lookahead === Lexer::T_TRUE): |
|
| 1938 | 124 | case ($lookahead === Lexer::T_FALSE): |
|
| 1939 | 3 | $this->match($lookahead); |
|
| 1940 | |||
| 1941 | 3 | return new AST\Literal(AST\Literal::BOOLEAN, $this->lexer->token['value']); |
|
| 1942 | |||
| 1943 | 124 | case ($lookahead === Lexer::T_INPUT_PARAMETER): |
|
| 1944 | switch (true) { |
||
| 1945 | case $this->isMathOperator($peek): |
||
| 1946 | // :param + u.value |
||
| 1947 | return $this->SimpleArithmeticExpression(); |
||
| 1948 | default: |
||
| 1949 | return $this->InputParameter(); |
||
| 1950 | } |
||
| 1951 | |||
| 1952 | 124 | case ($lookahead === Lexer::T_CASE): |
|
| 1953 | 120 | case ($lookahead === Lexer::T_COALESCE): |
|
| 1954 | 120 | case ($lookahead === Lexer::T_NULLIF): |
|
| 1955 | // Since NULLIF and COALESCE can be identified as a function, |
||
| 1956 | // we need to check these before checking for FunctionDeclaration |
||
| 1957 | 8 | return $this->CaseExpression(); |
|
| 1958 | |||
| 1959 | 120 | case ($lookahead === Lexer::T_OPEN_PARENTHESIS): |
|
| 1960 | 3 | return $this->SimpleArithmeticExpression(); |
|
| 1961 | |||
| 1962 | // this check must be done before checking for a filed path expression |
||
| 1963 | 117 | case ($this->isFunction()): |
|
| 1964 | 4 | $this->lexer->peek(); // "(" |
|
| 1965 | |||
| 1966 | switch (true) { |
||
| 1967 | 4 | case ($this->isMathOperator($this->peekBeyondClosingParenthesis())): |
|
| 1968 | // SUM(u.id) + COUNT(u.id) |
||
| 1969 | return $this->SimpleArithmeticExpression(); |
||
| 1970 | |||
| 1971 | default: |
||
| 1972 | // IDENTITY(u) |
||
| 1973 | 4 | return $this->FunctionDeclaration(); |
|
| 1974 | } |
||
| 1975 | |||
| 1976 | break; |
||
| 1977 | // it is no function, so it must be a field path |
||
| 1978 | 116 | case ($lookahead === Lexer::T_IDENTIFIER): |
|
| 1979 | 116 | $this->lexer->peek(); // lookahead => '.' |
|
| 1980 | 116 | $this->lexer->peek(); // lookahead => token after '.' |
|
| 1981 | 116 | $peek = $this->lexer->peek(); // lookahead => token after the token after the '.' |
|
| 1982 | 116 | $this->lexer->resetPeek(); |
|
| 1983 | |||
| 1984 | 116 | if ($this->isMathOperator($peek)) { |
|
| 1985 | 6 | return $this->SimpleArithmeticExpression(); |
|
| 1986 | } |
||
| 1987 | |||
| 1988 | 112 | return $this->StateFieldPathExpression(); |
|
| 1989 | |||
| 1990 | default: |
||
| 1991 | $this->syntaxError(); |
||
| 1992 | } |
||
| 1993 | } |
||
| 1994 | |||
| 1995 | /** |
||
| 1996 | * CaseExpression ::= GeneralCaseExpression | SimpleCaseExpression | CoalesceExpression | NullifExpression |
||
| 1997 | * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END" |
||
| 1998 | * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression |
||
| 1999 | * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END" |
||
| 2000 | * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator |
||
| 2001 | * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression |
||
| 2002 | * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")" |
||
| 2003 | * NullifExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")" |
||
| 2004 | * |
||
| 2005 | * @return mixed One of the possible expressions or subexpressions. |
||
| 2006 | */ |
||
| 2007 | 18 | public function CaseExpression() |
|
| 2035 | |||
| 2036 | /** |
||
| 2037 | * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")" |
||
| 2038 | * |
||
| 2039 | * @return \Doctrine\ORM\Query\AST\CoalesceExpression |
||
| 2040 | */ |
||
| 2041 | 3 | public function CoalesceExpression() |
|
| 2060 | |||
| 2061 | /** |
||
| 2062 | * NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")" |
||
| 2063 | * |
||
| 2064 | * @return \Doctrine\ORM\Query\AST\NullIfExpression |
||
| 2065 | */ |
||
| 2066 | 5 | public function NullIfExpression() |
|
| 2079 | |||
| 2080 | /** |
||
| 2081 | * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END" |
||
| 2082 | * |
||
| 2083 | * @return \Doctrine\ORM\Query\AST\GeneralCaseExpression |
||
| 2084 | */ |
||
| 2085 | 8 | public function GeneralCaseExpression() |
|
| 2102 | |||
| 2103 | /** |
||
| 2104 | * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END" |
||
| 2105 | * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator |
||
| 2106 | * |
||
| 2107 | * @return AST\SimpleCaseExpression |
||
| 2108 | */ |
||
| 2109 | 5 | public function SimpleCaseExpression() |
|
| 2127 | |||
| 2128 | /** |
||
| 2129 | * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression |
||
| 2130 | * |
||
| 2131 | * @return \Doctrine\ORM\Query\AST\WhenClause |
||
| 2132 | */ |
||
| 2133 | 8 | public function WhenClause() |
|
| 2141 | |||
| 2142 | /** |
||
| 2143 | * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression |
||
| 2144 | * |
||
| 2145 | * @return \Doctrine\ORM\Query\AST\SimpleWhenClause |
||
| 2146 | */ |
||
| 2147 | 5 | public function SimpleWhenClause() |
|
| 2155 | |||
| 2156 | /** |
||
| 2157 | * SelectExpression ::= ( |
||
| 2158 | * IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | |
||
| 2159 | * PartialObjectExpression | "(" Subselect ")" | CaseExpression | NewObjectExpression |
||
| 2160 | * ) [["AS"] ["HIDDEN"] AliasResultVariable] |
||
| 2161 | * |
||
| 2162 | * @return \Doctrine\ORM\Query\AST\SelectExpression |
||
| 2163 | */ |
||
| 2164 | 692 | public function SelectExpression() |
|
| 2165 | { |
||
| 2166 | 692 | $expression = null; |
|
| 2167 | 692 | $identVariable = null; |
|
| 2168 | 692 | $peek = $this->lexer->glimpse(); |
|
| 2169 | 692 | $lookaheadType = $this->lexer->lookahead['type']; |
|
| 2170 | |||
| 2171 | switch (true) { |
||
| 2172 | // ScalarExpression (u.name) |
||
| 2173 | 692 | case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] === Lexer::T_DOT): |
|
| 2174 | 91 | $expression = $this->ScalarExpression(); |
|
| 2175 | 91 | break; |
|
| 2176 | |||
| 2177 | // IdentificationVariable (u) |
||
| 2178 | 634 | case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] !== Lexer::T_OPEN_PARENTHESIS): |
|
| 2179 | 557 | $expression = $identVariable = $this->IdentificationVariable(); |
|
| 2180 | 557 | break; |
|
| 2181 | |||
| 2182 | // CaseExpression (CASE ... or NULLIF(...) or COALESCE(...)) |
||
| 2183 | 111 | case ($lookaheadType === Lexer::T_CASE): |
|
| 2184 | 106 | case ($lookaheadType === Lexer::T_COALESCE): |
|
| 2185 | 104 | case ($lookaheadType === Lexer::T_NULLIF): |
|
| 2186 | 9 | $expression = $this->CaseExpression(); |
|
| 2187 | 9 | break; |
|
| 2188 | |||
| 2189 | // DQL Function (SUM(u.value) or SUM(u.value) + 1) |
||
| 2190 | 102 | case ($this->isFunction()): |
|
| 2191 | 42 | $this->lexer->peek(); // "(" |
|
| 2192 | |||
| 2193 | switch (true) { |
||
| 2194 | 42 | case ($this->isMathOperator($this->peekBeyondClosingParenthesis())): |
|
| 2195 | // SUM(u.id) + COUNT(u.id) |
||
| 2196 | $expression = $this->ScalarExpression(); |
||
| 2197 | break; |
||
| 2198 | |||
| 2199 | default: |
||
| 2200 | // IDENTITY(u) |
||
| 2201 | 42 | $expression = $this->FunctionDeclaration(); |
|
| 2202 | 31 | break; |
|
| 2203 | } |
||
| 2204 | |||
| 2205 | 31 | break; |
|
| 2206 | |||
| 2207 | // PartialObjectExpression (PARTIAL u.{id, name}) |
||
| 2208 | 60 | case ($lookaheadType === Lexer::T_PARTIAL): |
|
| 2209 | 11 | $expression = $this->PartialObjectExpression(); |
|
| 2210 | 11 | $identVariable = $expression->identificationVariable; |
|
| 2211 | 11 | break; |
|
| 2212 | |||
| 2213 | // Subselect |
||
| 2214 | 49 | case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS && $peek['type'] === Lexer::T_SELECT): |
|
| 2215 | 5 | $this->match(Lexer::T_OPEN_PARENTHESIS); |
|
| 2216 | 5 | $expression = $this->Subselect(); |
|
| 2217 | 5 | $this->match(Lexer::T_CLOSE_PARENTHESIS); |
|
| 2218 | 5 | break; |
|
| 2219 | |||
| 2220 | // Shortcut: ScalarExpression => SimpleArithmeticExpression |
||
| 2221 | 44 | case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS): |
|
| 2222 | 41 | case ($lookaheadType === Lexer::T_INTEGER): |
|
| 2223 | 39 | case ($lookaheadType === Lexer::T_STRING): |
|
| 2224 | 30 | case ($lookaheadType === Lexer::T_FLOAT): |
|
| 2225 | // SimpleArithmeticExpression : (- u.value ) or ( + u.value ) |
||
| 2226 | 30 | case ($lookaheadType === Lexer::T_MINUS): |
|
| 2227 | 30 | case ($lookaheadType === Lexer::T_PLUS): |
|
| 2228 | 15 | $expression = $this->SimpleArithmeticExpression(); |
|
| 2229 | 15 | break; |
|
| 2230 | |||
| 2231 | // NewObjectExpression (New ClassName(id, name)) |
||
| 2232 | 29 | case ($lookaheadType === Lexer::T_NEW): |
|
| 2233 | 26 | $expression = $this->NewObjectExpression(); |
|
| 2234 | 26 | break; |
|
| 2235 | |||
| 2236 | default: |
||
| 2237 | 3 | $this->syntaxError( |
|
| 2238 | 3 | 'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression', |
|
| 2239 | 3 | $this->lexer->lookahead |
|
| 2240 | ); |
||
| 2241 | } |
||
| 2242 | |||
| 2243 | // [["AS"] ["HIDDEN"] AliasResultVariable] |
||
| 2244 | 685 | $mustHaveAliasResultVariable = false; |
|
| 2245 | |||
| 2246 | 685 | if ($this->lexer->isNextToken(Lexer::T_AS)) { |
|
| 2247 | 66 | $this->match(Lexer::T_AS); |
|
| 2248 | |||
| 2249 | 66 | $mustHaveAliasResultVariable = true; |
|
| 2250 | } |
||
| 2251 | |||
| 2252 | 685 | $hiddenAliasResultVariable = false; |
|
| 2253 | |||
| 2254 | 685 | if ($this->lexer->isNextToken(Lexer::T_HIDDEN)) { |
|
| 2255 | 2 | $this->match(Lexer::T_HIDDEN); |
|
| 2256 | |||
| 2257 | 2 | $hiddenAliasResultVariable = true; |
|
| 2258 | } |
||
| 2259 | |||
| 2260 | 685 | $aliasResultVariable = null; |
|
| 2261 | |||
| 2262 | 685 | if ($mustHaveAliasResultVariable || $this->lexer->isNextToken(Lexer::T_IDENTIFIER)) { |
|
| 2263 | 68 | $token = $this->lexer->lookahead; |
|
| 2264 | 68 | $aliasResultVariable = $this->AliasResultVariable(); |
|
| 2265 | |||
| 2266 | // Include AliasResultVariable in query components. |
||
| 2267 | 64 | $this->queryComponents[$aliasResultVariable] = [ |
|
| 2268 | 64 | 'resultVariable' => $expression, |
|
| 2269 | 64 | 'nestingLevel' => $this->nestingLevel, |
|
| 2270 | 64 | 'token' => $token, |
|
| 2271 | ]; |
||
| 2272 | } |
||
| 2273 | |||
| 2274 | // AST |
||
| 2275 | |||
| 2276 | 681 | $expr = new AST\SelectExpression($expression, $aliasResultVariable, $hiddenAliasResultVariable); |
|
| 2277 | |||
| 2278 | 681 | if ($identVariable) { |
|
| 2279 | 565 | $this->identVariableExpressions[$identVariable] = $expr; |
|
| 2280 | } |
||
| 2281 | |||
| 2282 | 681 | return $expr; |
|
| 2283 | } |
||
| 2284 | |||
| 2285 | /** |
||
| 2286 | * SimpleSelectExpression ::= ( |
||
| 2287 | * StateFieldPathExpression | IdentificationVariable | FunctionDeclaration | |
||
| 2288 | * AggregateExpression | "(" Subselect ")" | ScalarExpression |
||
| 2289 | * ) [["AS"] AliasResultVariable] |
||
| 2290 | * |
||
| 2291 | * @return \Doctrine\ORM\Query\AST\SimpleSelectExpression |
||
| 2292 | */ |
||
| 2293 | 27 | public function SimpleSelectExpression() |
|
| 2294 | { |
||
| 2295 | 27 | $peek = $this->lexer->glimpse(); |
|
| 2296 | |||
| 2297 | 27 | switch ($this->lexer->lookahead['type']) { |
|
| 2298 | 27 | case Lexer::T_IDENTIFIER: |
|
| 2299 | switch (true) { |
||
| 2300 | 19 | case ($peek['type'] === Lexer::T_DOT): |
|
| 2301 | 16 | $expression = $this->StateFieldPathExpression(); |
|
| 2302 | |||
| 2303 | 16 | return new AST\SimpleSelectExpression($expression); |
|
| 2304 | |||
| 2305 | 3 | case ($peek['type'] !== Lexer::T_OPEN_PARENTHESIS): |
|
| 2306 | 2 | $expression = $this->IdentificationVariable(); |
|
| 2307 | |||
| 2308 | 2 | return new AST\SimpleSelectExpression($expression); |
|
| 2309 | |||
| 2310 | 1 | case ($this->isFunction()): |
|
| 2311 | // SUM(u.id) + COUNT(u.id) |
||
| 2312 | 1 | if ($this->isMathOperator($this->peekBeyondClosingParenthesis())) { |
|
| 2313 | return new AST\SimpleSelectExpression($this->ScalarExpression()); |
||
| 2314 | } |
||
| 2315 | // COUNT(u.id) |
||
| 2316 | 1 | if ($this->isAggregateFunction($this->lexer->lookahead['type'])) { |
|
| 2317 | return new AST\SimpleSelectExpression($this->AggregateExpression()); |
||
| 2318 | } |
||
| 2319 | // IDENTITY(u) |
||
| 2320 | 1 | return new AST\SimpleSelectExpression($this->FunctionDeclaration()); |
|
| 2321 | |||
| 2322 | default: |
||
| 2323 | // Do nothing |
||
| 2324 | } |
||
| 2325 | break; |
||
| 2326 | |||
| 2327 | 8 | case Lexer::T_OPEN_PARENTHESIS: |
|
| 2328 | if ($peek['type'] !== Lexer::T_SELECT) { |
||
| 2329 | // Shortcut: ScalarExpression => SimpleArithmeticExpression |
||
| 2330 | $expression = $this->SimpleArithmeticExpression(); |
||
| 2331 | |||
| 2332 | return new AST\SimpleSelectExpression($expression); |
||
| 2333 | } |
||
| 2334 | |||
| 2335 | // Subselect |
||
| 2336 | $this->match(Lexer::T_OPEN_PARENTHESIS); |
||
| 2337 | $expression = $this->Subselect(); |
||
| 2338 | $this->match(Lexer::T_CLOSE_PARENTHESIS); |
||
| 2339 | |||
| 2340 | return new AST\SimpleSelectExpression($expression); |
||
| 2341 | |||
| 2342 | default: |
||
| 2343 | // Do nothing |
||
| 2344 | } |
||
| 2345 | |||
| 2346 | 8 | $this->lexer->peek(); |
|
| 2347 | |||
| 2348 | 8 | $expression = $this->ScalarExpression(); |
|
| 2349 | 7 | $expr = new AST\SimpleSelectExpression($expression); |
|
| 2350 | |||
| 2351 | 7 | if ($this->lexer->isNextToken(Lexer::T_AS)) { |
|
| 2352 | $this->match(Lexer::T_AS); |
||
| 2353 | } |
||
| 2354 | |||
| 2355 | 7 | if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER)) { |
|
| 2356 | $token = $this->lexer->lookahead; |
||
| 2357 | $resultVariable = $this->AliasResultVariable(); |
||
| 2358 | $expr->fieldIdentificationVariable = $resultVariable; |
||
| 2359 | |||
| 2360 | // Include AliasResultVariable in query components. |
||
| 2361 | $this->queryComponents[$resultVariable] = [ |
||
| 2362 | 'resultvariable' => $expr, |
||
| 2363 | 'nestingLevel' => $this->nestingLevel, |
||
| 2364 | 'token' => $token, |
||
| 2365 | ]; |
||
| 2366 | } |
||
| 2367 | |||
| 2368 | 7 | return $expr; |
|
| 2369 | } |
||
| 2370 | |||
| 2371 | /** |
||
| 2372 | * ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}* |
||
| 2373 | * |
||
| 2374 | * @return \Doctrine\ORM\Query\AST\ConditionalExpression |
||
| 2375 | */ |
||
| 2376 | 337 | public function ConditionalExpression() |
|
| 2395 | |||
| 2396 | /** |
||
| 2397 | * ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}* |
||
| 2398 | * |
||
| 2399 | * @return \Doctrine\ORM\Query\AST\ConditionalTerm |
||
| 2400 | */ |
||
| 2401 | 337 | public function ConditionalTerm() |
|
| 2420 | |||
| 2421 | /** |
||
| 2422 | * ConditionalFactor ::= ["NOT"] ConditionalPrimary |
||
| 2423 | * |
||
| 2424 | * @return \Doctrine\ORM\Query\AST\ConditionalFactor |
||
| 2425 | */ |
||
| 2426 | 337 | public function ConditionalFactor() |
|
| 2449 | |||
| 2450 | /** |
||
| 2451 | * ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")" |
||
| 2452 | * |
||
| 2453 | * @return \Doctrine\ORM\Query\AST\ConditionalPrimary |
||
| 2454 | */ |
||
| 2455 | 337 | public function ConditionalPrimary() |
|
| 2482 | |||
| 2483 | /** |
||
| 2484 | * SimpleConditionalExpression ::= |
||
| 2485 | * ComparisonExpression | BetweenExpression | LikeExpression | |
||
| 2486 | * InExpression | NullComparisonExpression | ExistsExpression | |
||
| 2487 | * EmptyCollectionComparisonExpression | CollectionMemberExpression | |
||
| 2488 | * InstanceOfExpression |
||
| 2489 | */ |
||
| 2490 | 337 | public function SimpleConditionalExpression() |
|
| 2579 | |||
| 2580 | /** |
||
| 2581 | * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY" |
||
| 2582 | * |
||
| 2583 | * @return \Doctrine\ORM\Query\AST\EmptyCollectionComparisonExpression |
||
| 2584 | */ |
||
| 2585 | 4 | public function EmptyCollectionComparisonExpression() |
|
| 2601 | |||
| 2602 | /** |
||
| 2603 | * CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression |
||
| 2604 | * |
||
| 2605 | * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression |
||
| 2606 | * SimpleEntityExpression ::= IdentificationVariable | InputParameter |
||
| 2607 | * |
||
| 2608 | * @return \Doctrine\ORM\Query\AST\CollectionMemberExpression |
||
| 2609 | */ |
||
| 2610 | 7 | public function CollectionMemberExpression() |
|
| 2634 | |||
| 2635 | /** |
||
| 2636 | * Literal ::= string | char | integer | float | boolean |
||
| 2637 | * |
||
| 2638 | * @return \Doctrine\ORM\Query\AST\Literal |
||
| 2639 | */ |
||
| 2640 | 146 | public function Literal() |
|
| 2665 | |||
| 2666 | /** |
||
| 2667 | * InParameter ::= Literal | InputParameter |
||
| 2668 | * |
||
| 2669 | * @return string | \Doctrine\ORM\Query\AST\InputParameter |
||
| 2670 | */ |
||
| 2671 | 25 | public function InParameter() |
|
| 2679 | |||
| 2680 | /** |
||
| 2681 | * InputParameter ::= PositionalParameter | NamedParameter |
||
| 2682 | * |
||
| 2683 | * @return \Doctrine\ORM\Query\AST\InputParameter |
||
| 2684 | */ |
||
| 2685 | 161 | public function InputParameter() |
|
| 2691 | |||
| 2692 | /** |
||
| 2693 | * ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")" |
||
| 2694 | * |
||
| 2695 | * @return \Doctrine\ORM\Query\AST\ArithmeticExpression |
||
| 2696 | */ |
||
| 2697 | 294 | public function ArithmeticExpression() |
|
| 2717 | |||
| 2718 | /** |
||
| 2719 | * SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}* |
||
| 2720 | * |
||
| 2721 | * @return \Doctrine\ORM\Query\AST\SimpleArithmeticExpression |
||
| 2722 | */ |
||
| 2723 | 364 | public function SimpleArithmeticExpression() |
|
| 2743 | |||
| 2744 | /** |
||
| 2745 | * ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}* |
||
| 2746 | * |
||
| 2747 | * @return \Doctrine\ORM\Query\AST\ArithmeticTerm |
||
| 2748 | */ |
||
| 2749 | 364 | public function ArithmeticTerm() |
|
| 2769 | |||
| 2770 | /** |
||
| 2771 | * ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary |
||
| 2772 | * |
||
| 2773 | * @return \Doctrine\ORM\Query\AST\ArithmeticFactor |
||
| 2774 | */ |
||
| 2775 | 364 | public function ArithmeticFactor() |
|
| 2794 | |||
| 2795 | /** |
||
| 2796 | * ArithmeticPrimary ::= SingleValuedPathExpression | Literal | ParenthesisExpression |
||
| 2797 | * | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings |
||
| 2798 | * | FunctionsReturningDatetime | IdentificationVariable | ResultVariable |
||
| 2799 | * | InputParameter | CaseExpression |
||
| 2800 | */ |
||
| 2801 | 368 | public function ArithmeticPrimary() |
|
| 2849 | |||
| 2850 | /** |
||
| 2851 | * StringExpression ::= StringPrimary | ResultVariable | "(" Subselect ")" |
||
| 2852 | * |
||
| 2853 | * @return \Doctrine\ORM\Query\AST\Subselect | |
||
| 2854 | * string |
||
| 2855 | */ |
||
| 2856 | 13 | public function StringExpression() |
|
| 2877 | |||
| 2878 | /** |
||
| 2879 | * StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression |
||
| 2880 | */ |
||
| 2881 | 49 | public function StringPrimary() |
|
| 2919 | |||
| 2920 | /** |
||
| 2921 | * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression |
||
| 2922 | * |
||
| 2923 | * @return \Doctrine\ORM\Query\AST\PathExpression | |
||
| 2924 | * \Doctrine\ORM\Query\AST\SimpleEntityExpression |
||
| 2925 | */ |
||
| 2926 | 7 | public function EntityExpression() |
|
| 2936 | |||
| 2937 | /** |
||
| 2938 | * SimpleEntityExpression ::= IdentificationVariable | InputParameter |
||
| 2939 | * |
||
| 2940 | * @return string | \Doctrine\ORM\Query\AST\InputParameter |
||
| 2941 | */ |
||
| 2942 | 6 | public function SimpleEntityExpression() |
|
| 2950 | |||
| 2951 | /** |
||
| 2952 | * AggregateExpression ::= |
||
| 2953 | * ("AVG" | "MAX" | "MIN" | "SUM" | "COUNT") "(" ["DISTINCT"] SimpleArithmeticExpression ")" |
||
| 2954 | * |
||
| 2955 | * @return \Doctrine\ORM\Query\AST\AggregateExpression |
||
| 2956 | */ |
||
| 2957 | 15 | public function AggregateExpression() |
|
| 2958 | { |
||
| 2959 | 15 | $lookaheadType = $this->lexer->lookahead['type']; |
|
| 2960 | 15 | $isDistinct = false; |
|
| 2961 | |||
| 2962 | 15 | if ( ! in_array($lookaheadType, [Lexer::T_COUNT, Lexer::T_AVG, Lexer::T_MAX, Lexer::T_MIN, Lexer::T_SUM])) { |
|
| 2963 | $this->syntaxError('One of: MAX, MIN, AVG, SUM, COUNT'); |
||
| 2964 | } |
||
| 2965 | |||
| 2966 | 15 | $this->match($lookaheadType); |
|
| 2967 | 15 | $functionName = $this->lexer->token['value']; |
|
| 2968 | 15 | $this->match(Lexer::T_OPEN_PARENTHESIS); |
|
| 2969 | |||
| 2970 | 15 | if ($this->lexer->isNextToken(Lexer::T_DISTINCT)) { |
|
| 2971 | $this->match(Lexer::T_DISTINCT); |
||
| 2972 | $isDistinct = true; |
||
| 2973 | } |
||
| 2974 | |||
| 2975 | 15 | $pathExp = $this->SimpleArithmeticExpression(); |
|
| 2976 | |||
| 2977 | 15 | $this->match(Lexer::T_CLOSE_PARENTHESIS); |
|
| 2978 | |||
| 2979 | 15 | return new AST\AggregateExpression($functionName, $pathExp, $isDistinct); |
|
| 2980 | } |
||
| 2981 | |||
| 2982 | /** |
||
| 2983 | * QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")" |
||
| 2984 | * |
||
| 2985 | * @return \Doctrine\ORM\Query\AST\QuantifiedExpression |
||
| 2986 | */ |
||
| 2987 | 3 | public function QuantifiedExpression() |
|
| 3006 | |||
| 3007 | /** |
||
| 3008 | * BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression |
||
| 3009 | * |
||
| 3010 | * @return \Doctrine\ORM\Query\AST\BetweenExpression |
||
| 3011 | */ |
||
| 3012 | 8 | public function BetweenExpression() |
|
| 3032 | |||
| 3033 | /** |
||
| 3034 | * ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression ) |
||
| 3035 | * |
||
| 3036 | * @return \Doctrine\ORM\Query\AST\ComparisonExpression |
||
| 3037 | */ |
||
| 3038 | 261 | public function ComparisonExpression() |
|
| 3050 | |||
| 3051 | /** |
||
| 3052 | * InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")" |
||
| 3053 | * |
||
| 3054 | * @return \Doctrine\ORM\Query\AST\InExpression |
||
| 3055 | */ |
||
| 3056 | 34 | public function InExpression() |
|
| 3086 | |||
| 3087 | /** |
||
| 3088 | * InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")") |
||
| 3089 | * |
||
| 3090 | * @return \Doctrine\ORM\Query\AST\InstanceOfExpression |
||
| 3091 | */ |
||
| 3092 | 12 | public function InstanceOfExpression() |
|
| 3130 | |||
| 3131 | /** |
||
| 3132 | * InstanceOfParameter ::= AbstractSchemaName | InputParameter |
||
| 3133 | * |
||
| 3134 | * @return mixed |
||
| 3135 | */ |
||
| 3136 | 12 | public function InstanceOfParameter() |
|
| 3150 | |||
| 3151 | /** |
||
| 3152 | * LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char] |
||
| 3153 | * |
||
| 3154 | * @return \Doctrine\ORM\Query\AST\LikeExpression |
||
| 3155 | */ |
||
| 3156 | 13 | public function LikeExpression() |
|
| 3189 | |||
| 3190 | /** |
||
| 3191 | * NullComparisonExpression ::= (InputParameter | NullIfExpression | CoalesceExpression | AggregateExpression | FunctionDeclaration | IdentificationVariable | SingleValuedPathExpression | ResultVariable) "IS" ["NOT"] "NULL" |
||
| 3192 | * |
||
| 3193 | * @return \Doctrine\ORM\Query\AST\NullComparisonExpression |
||
| 3194 | */ |
||
| 3195 | 11 | public function NullComparisonExpression() |
|
| 3196 | { |
||
| 3197 | switch (true) { |
||
| 3198 | 11 | case $this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER): |
|
| 3199 | $this->match(Lexer::T_INPUT_PARAMETER); |
||
| 3200 | |||
| 3201 | $expr = new AST\InputParameter($this->lexer->token['value']); |
||
| 3202 | break; |
||
| 3203 | |||
| 3204 | 11 | case $this->lexer->isNextToken(Lexer::T_NULLIF): |
|
| 3205 | 1 | $expr = $this->NullIfExpression(); |
|
| 3206 | 1 | break; |
|
| 3207 | |||
| 3208 | 11 | case $this->lexer->isNextToken(Lexer::T_COALESCE): |
|
| 3209 | 1 | $expr = $this->CoalesceExpression(); |
|
| 3210 | 1 | break; |
|
| 3211 | |||
| 3212 | 11 | case $this->isFunction(): |
|
| 3213 | 2 | $expr = $this->FunctionDeclaration(); |
|
| 3214 | 1 | break; |
|
| 3215 | |||
| 3216 | default: |
||
| 3217 | // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression |
||
| 3218 | 10 | $glimpse = $this->lexer->glimpse(); |
|
| 3219 | |||
| 3220 | 10 | if ($glimpse['type'] === Lexer::T_DOT) { |
|
| 3221 | 9 | $expr = $this->SingleValuedPathExpression(); |
|
| 3222 | |||
| 3223 | // Leave switch statement |
||
| 3224 | 9 | break; |
|
| 3225 | } |
||
| 3226 | |||
| 3227 | 1 | $lookaheadValue = $this->lexer->lookahead['value']; |
|
| 3228 | |||
| 3229 | // Validate existing component |
||
| 3230 | 1 | if ( ! isset($this->queryComponents[$lookaheadValue])) { |
|
| 3231 | $this->semanticalError('Cannot add having condition on undefined result variable.'); |
||
| 3232 | } |
||
| 3233 | |||
| 3234 | // Validate SingleValuedPathExpression (ie.: "product") |
||
| 3235 | 1 | if (isset($this->queryComponents[$lookaheadValue]['metadata'])) { |
|
| 3236 | 1 | $expr = $this->SingleValuedPathExpression(); |
|
| 3237 | 1 | break; |
|
| 3238 | } |
||
| 3239 | |||
| 3240 | // Validating ResultVariable |
||
| 3241 | if ( ! isset($this->queryComponents[$lookaheadValue]['resultVariable'])) { |
||
| 3242 | $this->semanticalError('Cannot add having condition on a non result variable.'); |
||
| 3243 | } |
||
| 3244 | |||
| 3245 | $expr = $this->ResultVariable(); |
||
| 3246 | break; |
||
| 3247 | } |
||
| 3248 | |||
| 3249 | 11 | $nullCompExpr = new AST\NullComparisonExpression($expr); |
|
| 3250 | |||
| 3251 | 11 | $this->match(Lexer::T_IS); |
|
| 3252 | |||
| 3253 | 11 | if ($this->lexer->isNextToken(Lexer::T_NOT)) { |
|
| 3254 | 3 | $this->match(Lexer::T_NOT); |
|
| 3255 | |||
| 3256 | 3 | $nullCompExpr->not = true; |
|
| 3257 | } |
||
| 3258 | |||
| 3259 | 11 | $this->match(Lexer::T_NULL); |
|
| 3260 | |||
| 3261 | 11 | return $nullCompExpr; |
|
| 3262 | } |
||
| 3263 | |||
| 3264 | /** |
||
| 3265 | * ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")" |
||
| 3266 | * |
||
| 3267 | * @return \Doctrine\ORM\Query\AST\ExistsExpression |
||
| 3268 | */ |
||
| 3269 | 7 | public function ExistsExpression() |
|
| 3288 | |||
| 3289 | /** |
||
| 3290 | * ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!=" |
||
| 3291 | * |
||
| 3292 | * @return string |
||
| 3293 | */ |
||
| 3294 | 259 | public function ComparisonOperator() |
|
| 3337 | |||
| 3338 | /** |
||
| 3339 | * FunctionDeclaration ::= FunctionsReturningStrings | FunctionsReturningNumerics | FunctionsReturningDatetime |
||
| 3340 | * |
||
| 3341 | * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode |
||
| 3342 | */ |
||
| 3343 | 71 | public function FunctionDeclaration() |
|
| 3368 | |||
| 3369 | /** |
||
| 3370 | * Helper function for FunctionDeclaration grammar rule. |
||
| 3371 | * |
||
| 3372 | * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode |
||
| 3373 | */ |
||
| 3374 | 71 | private function CustomFunctionDeclaration() |
|
| 3396 | |||
| 3397 | /** |
||
| 3398 | * FunctionsReturningNumerics ::= |
||
| 3399 | * "LENGTH" "(" StringPrimary ")" | |
||
| 3400 | * "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" | |
||
| 3401 | * "ABS" "(" SimpleArithmeticExpression ")" | |
||
| 3402 | * "SQRT" "(" SimpleArithmeticExpression ")" | |
||
| 3403 | * "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" | |
||
| 3404 | * "SIZE" "(" CollectionValuedPathExpression ")" | |
||
| 3405 | * "DATE_DIFF" "(" ArithmeticPrimary "," ArithmeticPrimary ")" | |
||
| 3406 | * "BIT_AND" "(" ArithmeticPrimary "," ArithmeticPrimary ")" | |
||
| 3407 | * "BIT_OR" "(" ArithmeticPrimary "," ArithmeticPrimary ")" |
||
| 3408 | * |
||
| 3409 | * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode |
||
| 3410 | */ |
||
| 3411 | 37 | public function FunctionsReturningNumerics() |
|
| 3421 | |||
| 3422 | /** |
||
| 3423 | * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode |
||
| 3424 | */ |
||
| 3425 | 2 | public function CustomFunctionsReturningNumerics() |
|
| 3439 | |||
| 3440 | /** |
||
| 3441 | * FunctionsReturningDateTime ::= |
||
| 3442 | * "CURRENT_DATE" | |
||
| 3443 | * "CURRENT_TIME" | |
||
| 3444 | * "CURRENT_TIMESTAMP" | |
||
| 3445 | * "DATE_ADD" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" | |
||
| 3446 | * "DATE_SUB" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" |
||
| 3447 | * |
||
| 3448 | * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode |
||
| 3449 | */ |
||
| 3450 | 7 | public function FunctionsReturningDatetime() |
|
| 3460 | |||
| 3461 | /** |
||
| 3462 | * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode |
||
| 3463 | */ |
||
| 3464 | public function CustomFunctionsReturningDatetime() |
||
| 3478 | |||
| 3479 | /** |
||
| 3480 | * FunctionsReturningStrings ::= |
||
| 3481 | * "CONCAT" "(" StringPrimary "," StringPrimary {"," StringPrimary}* ")" | |
||
| 3482 | * "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" | |
||
| 3483 | * "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" | |
||
| 3484 | * "LOWER" "(" StringPrimary ")" | |
||
| 3485 | * "UPPER" "(" StringPrimary ")" | |
||
| 3486 | * "IDENTITY" "(" SingleValuedAssociationPathExpression {"," string} ")" |
||
| 3487 | * |
||
| 3488 | * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode |
||
| 3489 | */ |
||
| 3490 | 29 | public function FunctionsReturningStrings() |
|
| 3500 | |||
| 3501 | /** |
||
| 3502 | * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode |
||
| 3503 | */ |
||
| 3504 | 2 | public function CustomFunctionsReturningStrings() |
|
| 3518 | } |
||
| 3519 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..