diff --git a/lib/Doctrine/ORM/Query/Lexer.php b/lib/Doctrine/ORM/Query/Lexer.php index fbff39e4c..6ad701985 100644 --- a/lib/Doctrine/ORM/Query/Lexer.php +++ b/lib/Doctrine/ORM/Query/Lexer.php @@ -49,48 +49,58 @@ class Lexer extends \Doctrine\Common\Lexer const T_AVG = 106; const T_BETWEEN = 107; const T_BY = 108; - const T_COMMA = 109; - const T_COUNT = 110; - const T_DELETE = 111; - const T_DESC = 112; - const T_DISTINCT = 113; - const T_DOT = 114; - const T_EMPTY = 115; - const T_ESCAPE = 116; - const T_EXISTS = 117; - const T_FROM = 118; - const T_GROUP = 119; - const T_HAVING = 120; - const T_IN = 121; - const T_INDEX = 122; - const T_INNER = 123; - const T_IS = 124; - const T_JOIN = 125; - const T_LEFT = 126; - const T_LIKE = 127; - const T_LIMIT = 128; - const T_MAX = 129; - const T_MIN = 130; - const T_MOD = 131; - const T_NOT = 132; - const T_NULL = 133; - const T_OFFSET = 134; - const T_ON = 135; - const T_OR = 136; - const T_ORDER = 137; - const T_OUTER = 138; - const T_SELECT = 139; - const T_SET = 140; - const T_SIZE = 141; - const T_SOME = 142; - const T_SUM = 143; - const T_UPDATE = 144; - const T_WHERE = 145; - const T_WITH = 146; - const T_TRUE = 147; - const T_FALSE = 148; - const T_MEMBER = 149; - const T_OF = 150; + const T_CLOSE_PARENTHESIS = 109; + const T_COMMA = 110; + const T_COUNT = 111; + const T_DELETE = 112; + const T_DESC = 113; + const T_DISTINCT = 114; + const T_DIVIDE = 115; + const T_DOT = 116; + const T_EMPTY = 117; + const T_EQUALS = 118; + const T_ESCAPE = 119; + const T_EXISTS = 120; + const T_FALSE = 121; + const T_FROM = 122; + const T_GREATER_THAN = 123; + const T_GROUP = 124; + const T_HAVING = 125; + const T_IN = 126; + const T_INDEX = 127; + const T_INNER = 128; + const T_IS = 129; + const T_JOIN = 130; + const T_LEFT = 131; + const T_LIKE = 132; + const T_LIMIT = 133; + const T_LOWER_THAN = 134; + const T_MAX = 135; + const T_MEMBER = 136; + const T_MIN = 137; + const T_MINUS = 138; + const T_MOD = 139; + const T_MULTIPLY = 140; + const T_NEGATE = 141; + const T_NOT = 142; + const T_NULL = 143; + const T_OF = 144; + const T_OFFSET = 145; + const T_ON = 146; + const T_OPEN_PARENTHESIS = 147; + const T_OR = 148; + const T_ORDER = 149; + const T_OUTER = 150; + const T_PLUS = 151; + const T_SELECT = 152; + const T_SET = 153; + const T_SIZE = 154; + const T_SOME = 155; + const T_SUM = 156; + const T_TRUE = 157; + const T_UPDATE = 158; + const T_WHERE = 159; + const T_WITH = 160; private $_keywordsTable; @@ -131,23 +141,42 @@ class Lexer extends \Doctrine\Common\Lexer protected function _getType(&$value) { $type = self::T_NONE; - $newVal = $this->_getNumeric($value); + + // Recognizing numeric values if ($newVal !== false){ $value = $newVal; - if (strpos($value, '.') !== false || stripos($value, 'e') !== false) { - $type = self::T_FLOAT; - } else { - $type = self::T_INTEGER; - } + + return (strpos($value, '.') !== false || stripos($value, 'e') !== false) + ? self::T_FLOAT : self::T_INTEGER; } + if ($value[0] === "'") { - $type = self::T_STRING; $value = str_replace("''", "'", substr($value, 1, strlen($value) - 2)); + + return self::T_STRING; } else if (ctype_alpha($value[0]) || $value[0] === '_') { - $type = $this->_checkLiteral($value); + return $this->_checkLiteral($value); } else if ($value[0] === '?' || $value[0] === ':') { - $type = self::T_INPUT_PARAMETER; + return self::T_INPUT_PARAMETER; + } else { + switch ($value) { + case '.': return self::T_DOT; + case ',': return self::T_COMMA; + case '(': return self::T_OPEN_PARENTHESIS; + case ')': return self::T_CLOSE_PARENTHESIS; + case '=': return self::T_EQUALS; + case '>': return self::T_GREATER_THAN; + case '<': return self::T_LOWER_THAN; + case '+': return self::T_PLUS; + case '-': return self::T_MINUS; + case '*': return self::T_MULTIPLY; + case '/': return self::T_DIVIDE; + case '!': return self::T_NEGATE; + default: + // Do nothing + break; + } } return $type; diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index f423f1ca4..60e4c5767 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -41,27 +41,27 @@ class Parser { /** Maps registered string function names to class names. */ private static $_STRING_FUNCTIONS = array( - 'concat' => 'Doctrine\ORM\Query\AST\Functions\ConcatFunction', + 'concat' => 'Doctrine\ORM\Query\AST\Functions\ConcatFunction', 'substring' => 'Doctrine\ORM\Query\AST\Functions\SubstringFunction', - 'trim' => 'Doctrine\ORM\Query\AST\Functions\TrimFunction', - 'lower' => 'Doctrine\ORM\Query\AST\Functions\LowerFunction', - 'upper' => 'Doctrine\ORM\Query\AST\Functions\UpperFunction' + 'trim' => 'Doctrine\ORM\Query\AST\Functions\TrimFunction', + 'lower' => 'Doctrine\ORM\Query\AST\Functions\LowerFunction', + 'upper' => 'Doctrine\ORM\Query\AST\Functions\UpperFunction' ); /** Maps registered numeric function names to class names. */ private static $_NUMERIC_FUNCTIONS = array( 'length' => 'Doctrine\ORM\Query\AST\Functions\LengthFunction', 'locate' => 'Doctrine\ORM\Query\AST\Functions\LocateFunction', - 'abs' => 'Doctrine\ORM\Query\AST\Functions\AbsFunction', - 'sqrt' => 'Doctrine\ORM\Query\AST\Functions\SqrtFunction', - 'mod' => 'Doctrine\ORM\Query\AST\Functions\ModFunction', - 'size' => 'Doctrine\ORM\Query\AST\Functions\SizeFunction' + 'abs' => 'Doctrine\ORM\Query\AST\Functions\AbsFunction', + 'sqrt' => 'Doctrine\ORM\Query\AST\Functions\SqrtFunction', + 'mod' => 'Doctrine\ORM\Query\AST\Functions\ModFunction', + 'size' => 'Doctrine\ORM\Query\AST\Functions\SizeFunction' ); /** Maps registered datetime function names to class names. */ private static $_DATETIME_FUNCTIONS = array( - 'current_date' => 'Doctrine\ORM\Query\AST\Functions\CurrentDateFunction', - 'current_time' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimeFunction', + 'current_date' => 'Doctrine\ORM\Query\AST\Functions\CurrentDateFunction', + 'current_time' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimeFunction', 'current_timestamp' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimestampFunction' ); @@ -792,7 +792,7 @@ class Parser { $token = $this->_lexer->lookahead; $identVariable = $this->IdentificationVariable(); - $this->match('.'); + $this->match(Lexer::T_DOT); $this->match(Lexer::T_IDENTIFIER); $field = $this->_lexer->token['value']; @@ -825,11 +825,11 @@ class Parser $parts = array(); do { - $this->match('.'); + $this->match(Lexer::T_DOT); $this->match(Lexer::T_IDENTIFIER); $parts[] = $this->_lexer->token['value']; - } while ($this->_lexer->isNextToken('.')); + } while ($this->_lexer->isNextToken(Lexer::T_DOT)); // Creating AST node $pathExpr = new AST\PathExpression($expectedType, $identVariable, $parts); @@ -950,8 +950,8 @@ class Parser $selectExpressions = array(); $selectExpressions[] = $this->SelectExpression(); - while ($this->_lexer->isNextToken(',')) { - $this->match(','); + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); $selectExpressions[] = $this->SelectExpression(); } @@ -1016,8 +1016,8 @@ class Parser $updateItems = array(); $updateItems[] = $this->UpdateItem(); - while ($this->_lexer->isNextToken(',')) { - $this->match(','); + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); $updateItems[] = $this->UpdateItem(); } @@ -1083,8 +1083,8 @@ class Parser $identificationVariableDeclarations = array(); $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration(); - while ($this->_lexer->isNextToken(',')) { - $this->match(','); + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration(); } @@ -1102,8 +1102,8 @@ class Parser $identificationVariables = array(); $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration(); - while ($this->_lexer->isNextToken(',')) { - $this->match(','); + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration(); } @@ -1146,8 +1146,8 @@ class Parser $groupByItems = array($this->GroupByItem()); - while ($this->_lexer->isNextToken(',')) { - $this->match(','); + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); $groupByItems[] = $this->GroupByItem(); } @@ -1167,8 +1167,8 @@ class Parser $orderByItems = array(); $orderByItems[] = $this->OrderByItem(); - while ($this->_lexer->isNextToken(',')) { - $this->match(','); + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); $orderByItems[] = $this->OrderByItem(); } @@ -1223,7 +1223,7 @@ class Parser // Validate if IdentificationVariable is defined $queryComponent = $this->_validateIdentificationVariable($identVariable, null, $token); - $this->match('.'); + $this->match(Lexer::T_DOT); $this->match(Lexer::T_IDENTIFIER); $field = $this->_lexer->token['value']; @@ -1236,7 +1236,7 @@ class Parser ); } - $this->match('='); + $this->match(Lexer::T_EQUALS); $newValue = $this->NewValue(); @@ -1551,9 +1551,9 @@ class Parser $expression = $this->FunctionDeclaration(); } } else { - $this->match('('); + $this->match(Lexer::T_OPEN_PARENTHESIS); $expression = $this->Subselect(); - $this->match(')'); + $this->match(Lexer::T_CLOSE_PARENTHESIS); } if ($this->_lexer->isNextToken(Lexer::T_AS)) { @@ -1601,6 +1601,8 @@ class Parser return new AST\SimpleSelectExpression($this->StateFieldPathExpression()); } + // TODO Fix this!!! + echo 'SimpleSelectExpression: '; var_dump($this->_lexer->lookahead); $this->match($this->_lexer->lookahead['value']); return new AST\SimpleSelectExpression($this->_lexer->token['value']); @@ -1717,9 +1719,9 @@ class Parser $peek['type'] === Lexer::T_EXISTS) { $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression(); } else { - $this->match('('); + $this->match(Lexer::T_OPEN_PARENTHESIS); $condPrimary->conditionalExpression = $this->ConditionalExpression(); - $this->match(')'); + $this->match(Lexer::T_CLOSE_PARENTHESIS); } } else { $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression(); @@ -1778,7 +1780,11 @@ class Parser if ($pathExprOrInputParam) { switch ($token['type']) { - case Lexer::T_NONE: + case Lexer::T_EQUALS: + case Lexer::T_LOWER_THAN: + case Lexer::T_GREATER_THAN: + case Lexer::T_NEGATE: + case Lexer::T_OPEN_PARENTHESIS: return $this->ComparisonExpression(); case Lexer::T_BETWEEN: @@ -1874,16 +1880,23 @@ class Parser { switch ($this->_lexer->lookahead['type']) { case Lexer::T_STRING: - $this->match($this->_lexer->lookahead['value']); + $this->match(Lexer::T_STRING); return new AST\Literal(AST\Literal::STRING, $this->_lexer->token['value']); + case Lexer::T_INTEGER: case Lexer::T_FLOAT: - $this->match($this->_lexer->lookahead['value']); + $this->match( + $this->_lexer->isNextToken(Lexer::T_INTEGER) ? Lexer::T_INTEGER : Lexer::T_FLOAT + ); return new AST\Literal(AST\Literal::NUMERIC, $this->_lexer->token['value']); + case Lexer::T_TRUE: case Lexer::T_FALSE: - $this->match($this->_lexer->lookahead['value']); + $this->match( + $this->_lexer->isNextToken(Lexer::T_TRUE) ? Lexer::T_TRUE : Lexer::T_FALSE + ); return new AST\Literal(AST\Literal::BOOLEAN, $this->_lexer->token['value']); + default: $this->syntaxError('Literal'); } @@ -1911,7 +1924,7 @@ class Parser */ public function InputParameter() { - $this->match($this->_lexer->lookahead['value']); + $this->match(Lexer::T_INPUT_PARAMETER); return new AST\InputParameter($this->_lexer->token['value']); } @@ -1926,13 +1939,13 @@ class Parser { $expr = new AST\ArithmeticExpression; - if ($this->_lexer->lookahead['value'] === '(') { + if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { $peek = $this->_lexer->glimpse(); if ($peek['type'] === Lexer::T_SELECT) { - $this->match('('); + $this->match(Lexer::T_OPEN_PARENTHESIS); $expr->subselect = $this->Subselect(); - $this->match(')'); + $this->match(Lexer::T_CLOSE_PARENTHESIS); return $expr; } @@ -1953,12 +1966,8 @@ class Parser $terms = array(); $terms[] = $this->ArithmeticTerm(); - while ($this->_lexer->lookahead['value'] == '+' || $this->_lexer->lookahead['value'] == '-') { - if ($this->_lexer->lookahead['value'] == '+') { - $this->match('+'); - } else { - $this->match('-'); - } + while (($isPlus = $this->_lexer->isNextToken(Lexer::T_PLUS)) || $this->_lexer->isNextToken(Lexer::T_MINUS)) { + $this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS); $terms[] = $this->_lexer->token['value']; $terms[] = $this->ArithmeticTerm(); @@ -1977,13 +1986,9 @@ class Parser $factors = array(); $factors[] = $this->ArithmeticFactor(); - while ($this->_lexer->lookahead['value'] == '*' || $this->_lexer->lookahead['value'] == '/') { - if ($this->_lexer->lookahead['value'] == '*') { - $this->match('*'); - } else { - $this->match('/'); - } - + while (($isMult = $this->_lexer->isNextToken(Lexer::T_MULTIPLY)) || $this->_lexer->isNextToken(Lexer::T_DIVIDE)) { + $this->match(($isMult) ? Lexer::T_MULTIPLY : Lexer::T_DIVIDE); + $factors[] = $this->_lexer->token['value']; $factors[] = $this->ArithmeticFactor(); } @@ -1998,16 +2003,13 @@ class Parser */ public function ArithmeticFactor() { - $sign = null; - - if ($this->_lexer->lookahead['value'] == '+') { - $this->match('+'); - $sign = true; - } else if ($this->_lexer->lookahead['value'] == '-') { - $this->match('-'); - $sign = false; - } + $sign = null; + if (($isPlus = $this->_lexer->isNextToken(Lexer::T_PLUS)) || $this->_lexer->isNextToken(Lexer::T_MINUS)) { + $this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS); + $sign = $isPlus; + } + return new AST\ArithmeticFactor($this->ArithmeticPrimary(), $sign); } @@ -2018,10 +2020,10 @@ class Parser */ public function ArithmeticPrimary() { - if ($this->_lexer->lookahead['value'] === '(') { - $this->match('('); + if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + $this->match(Lexer::T_OPEN_PARENTHESIS); $expr = $this->SimpleArithmeticExpression(); - $this->match(')'); + $this->match(Lexer::T_CLOSE_PARENTHESIS); return $expr; } @@ -2066,13 +2068,13 @@ class Parser */ public function StringExpression() { - if ($this->_lexer->lookahead['value'] === '(') { + if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { $peek = $this->_lexer->glimpse(); if ($peek['type'] === Lexer::T_SELECT) { - $this->match('('); + $this->match(Lexer::T_OPEN_PARENTHESIS); $expr = $this->Subselect(); - $this->match(')'); + $this->match(Lexer::T_CLOSE_PARENTHESIS); return $expr; } @@ -2086,7 +2088,7 @@ class Parser */ public function StringPrimary() { - if ($this->_lexer->lookahead['type'] === Lexer::T_IDENTIFIER) { + if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) { $peek = $this->_lexer->glimpse(); if ($peek['value'] == '.') { @@ -2096,11 +2098,11 @@ class Parser } else { $this->syntaxError("'.' or '('"); } - } else if ($this->_lexer->lookahead['type'] === Lexer::T_STRING) { + } else if ($this->_lexer->isNextToken(Lexer::T_STRING)) { $this->match(Lexer::T_STRING); return $this->_lexer->token['value']; - } else if ($this->_lexer->lookahead['type'] === Lexer::T_INPUT_PARAMETER) { + } else if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { return $this->InputParameter(); } else if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { return $this->AggregateExpression(); @@ -2156,7 +2158,7 @@ class Parser if ($this->_lexer->isNextToken(Lexer::T_COUNT)) { $this->match(Lexer::T_COUNT); $functionName = $this->_lexer->token['value']; - $this->match('('); + $this->match(Lexer::T_OPEN_PARENTHESIS); if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) { $this->match(Lexer::T_DISTINCT); @@ -2164,7 +2166,7 @@ class Parser } $pathExp = $this->SingleValuedPathExpression(); - $this->match(')'); + $this->match(Lexer::T_CLOSE_PARENTHESIS); } else { if ($this->_lexer->isNextToken(Lexer::T_AVG)) { $this->match(Lexer::T_AVG); @@ -2179,9 +2181,9 @@ class Parser } $functionName = $this->_lexer->token['value']; - $this->match('('); + $this->match(Lexer::T_OPEN_PARENTHESIS); $pathExp = $this->StateFieldPathExpression(); - $this->match(')'); + $this->match(Lexer::T_CLOSE_PARENTHESIS); } return new AST\AggregateExpression($functionName, $pathExp, $isDistinct); @@ -2210,10 +2212,10 @@ class Parser $this->syntaxError('ALL, ANY or SOME'); } - $this->match('('); + $this->match(Lexer::T_OPEN_PARENTHESIS); $qExpr = new AST\QuantifiedExpression($this->Subselect()); $qExpr->type = $type; - $this->match(')'); + $this->match(Lexer::T_CLOSE_PARENTHESIS); return $qExpr; } @@ -2280,7 +2282,7 @@ class Parser } $this->match(Lexer::T_IN); - $this->match('('); + $this->match(Lexer::T_OPEN_PARENTHESIS); if ($this->_lexer->isNextToken(Lexer::T_SELECT)) { $inExpression->subselect = $this->Subselect(); @@ -2288,15 +2290,15 @@ class Parser $literals = array(); $literals[] = $this->InParameter(); - while ($this->_lexer->isNextToken(',')) { - $this->match(','); + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); $literals[] = $this->InParameter(); } $inExpression->literals = $literals; } - $this->match(')'); + $this->match(Lexer::T_CLOSE_PARENTHESIS); return $inExpression; } @@ -2311,7 +2313,7 @@ class Parser $stringExpr = $this->StringExpression(); $not = false; - if ($this->_lexer->lookahead['type'] === Lexer::T_NOT) { + if ($this->_lexer->isNextToken(Lexer::T_NOT)) { $this->match(Lexer::T_NOT); $not = true; } @@ -2382,10 +2384,10 @@ class Parser } $this->match(Lexer::T_EXISTS); - $this->match('('); + $this->match(Lexer::T_OPEN_PARENTHESIS); $existsExpression = new AST\ExistsExpression($this->Subselect()); $existsExpression->not = $not; - $this->match(')'); + $this->match(Lexer::T_CLOSE_PARENTHESIS); return $existsExpression; } @@ -2399,38 +2401,38 @@ class Parser { switch ($this->_lexer->lookahead['value']) { case '=': - $this->match('='); + $this->match(Lexer::T_EQUALS); return '='; case '<': - $this->match('<'); + $this->match(Lexer::T_LOWER_THAN); $operator = '<'; - if ($this->_lexer->isNextToken('=')) { - $this->match('='); + if ($this->_lexer->isNextToken(Lexer::T_EQUALS)) { + $this->match(Lexer::T_EQUALS); $operator .= '='; - } else if ($this->_lexer->isNextToken('>')) { - $this->match('>'); + } else if ($this->_lexer->isNextToken(Lexer::T_GREATER_THAN)) { + $this->match(Lexer::T_GREATER_THAN); $operator .= '>'; } return $operator; case '>': - $this->match('>'); + $this->match(Lexer::T_GREATER_THAN); $operator = '>'; - if ($this->_lexer->isNextToken('=')) { - $this->match('='); + if ($this->_lexer->isNextToken(Lexer::T_EQUALS)) { + $this->match(Lexer::T_EQUALS); $operator .= '='; } return $operator; case '!': - $this->match('!'); - $this->match('='); + $this->match(Lexer::T_NEGATE); + $this->match(Lexer::T_EQUALS); return '<>'; diff --git a/tests/Doctrine/Tests/ORM/Query/LexerTest.php b/tests/Doctrine/Tests/ORM/Query/LexerTest.php index b50e82081..f84669554 100644 --- a/tests/Doctrine/Tests/ORM/Query/LexerTest.php +++ b/tests/Doctrine/Tests/ORM/Query/LexerTest.php @@ -112,7 +112,7 @@ class LexerTest extends \Doctrine\Tests\OrmTestCase $lexer = new Lexer('- 1.234e2'); $lexer->moveNext(); $token = $lexer->lookahead; - $this->assertEquals(Lexer::T_NONE, $token['type']); + $this->assertEquals(Lexer::T_MINUS, $token['type']); $this->assertEquals('-', $token['value']); $lexer->moveNext(); @@ -201,7 +201,7 @@ class LexerTest extends \Doctrine\Tests\OrmTestCase ), array( 'value' => '.', - 'type' => Lexer::T_NONE, + 'type' => Lexer::T_DOT, 'position' => 41 ), array( @@ -211,7 +211,7 @@ class LexerTest extends \Doctrine\Tests\OrmTestCase ), array( 'value' => '=', - 'type' => Lexer::T_NONE, + 'type' => Lexer::T_EQUALS, 'position' => 47 ), array(