diff --git a/lib/Doctrine/ORM/Query/Lexer.php b/lib/Doctrine/ORM/Query/Lexer.php index 7fe7ecb50..4c6645b04 100644 --- a/lib/Doctrine/ORM/Query/Lexer.php +++ b/lib/Doctrine/ORM/Query/Lexer.php @@ -1,7 +1,5 @@ - * @author Janne Vanhala - * @author Roman Borschel - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.doctrine-project.org - * @since 2.0 - * @version $Revision$ + * @author Guilherme Blanco + * @author Janne Vanhala + * @author Roman Borschel + * @since 2.0 */ class Lexer extends \Doctrine\Common\Lexer { + // All tokens that are not valid identifiers must be < 100 const T_NONE = 1; - const T_IDENTIFIER = 2; - const T_INTEGER = 3; - const T_STRING = 4; - const T_INPUT_PARAMETER = 5; - const T_FLOAT = 6; - + const T_INTEGER = 2; + const T_STRING = 3; + const T_INPUT_PARAMETER = 4; + const T_FLOAT = 5; + const T_CLOSE_PARENTHESIS = 6; + const T_OPEN_PARENTHESIS = 7; + const T_COMMA = 8; + const T_DIVIDE = 9; + const T_DOT = 10; + const T_EQUALS = 11; + const T_GREATER_THAN = 12; + const T_LOWER_THAN = 13; + const T_MINUS = 14; + const T_MULTIPLY = 15; + const T_NEGATE = 16; + const T_PLUS = 17; + const T_OPEN_CURLY_BRACE = 18; + const T_CLOSE_CURLY_BRACE = 19; + + // All tokens that are also identifiers should be >= 100 + const T_IDENTIFIER = 100; const T_ALL = 101; const T_AND = 102; const T_ANY = 103; @@ -50,62 +61,46 @@ class Lexer extends \Doctrine\Common\Lexer const T_BETWEEN = 107; const T_BOTH = 108; const T_BY = 109; - const T_CLOSE_PARENTHESIS = 110; - const T_COMMA = 111; - const T_COUNT = 112; - const T_DELETE = 113; - const T_DESC = 114; - const T_DISTINCT = 115; - const T_DIVIDE = 116; - const T_DOT = 117; - const T_EMPTY = 118; - const T_EQUALS = 119; - const T_ESCAPE = 120; - const T_EXISTS = 121; - const T_FALSE = 122; - const T_FROM = 123; - const T_GREATER_THAN = 124; - const T_GROUP = 125; - const T_HAVING = 126; - const T_IN = 127; - const T_INDEX = 128; - const T_INNER = 129; - const T_IS = 130; - const T_JOIN = 131; - const T_LEADING = 132; - const T_LEFT = 133; - const T_LIKE = 134; - const T_LIMIT = 135; - const T_LOWER_THAN = 136; - const T_MAX = 137; - const T_MEMBER = 138; - const T_MIN = 139; - const T_MINUS = 140; - const T_MOD = 141; - const T_MULTIPLY = 142; - const T_NEGATE = 143; - const T_NOT = 144; - const T_NULL = 145; - const T_OF = 146; - const T_OFFSET = 147; - const T_OPEN_PARENTHESIS = 149; - const T_OR = 150; - const T_ORDER = 151; - const T_OUTER = 152; - const T_PLUS = 153; - const T_SELECT = 154; - const T_SET = 155; - const T_SIZE = 156; - const T_SOME = 157; - const T_SUM = 158; - const T_TRAILING = 159; - const T_TRUE = 160; - const T_UPDATE = 161; - const T_WHERE = 162; - const T_WITH = 163; - const T_PARTIAL = 164; - const T_OPEN_CURLY_BRACE = 165; - const T_CLOSE_CURLY_BRACE = 166; + const T_COUNT = 110; + const T_DELETE = 111; + const T_DESC = 112; + const T_DISTINCT = 113; + const T_EMPTY = 114; + const T_ESCAPE = 115; + const T_EXISTS = 116; + const T_FALSE = 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_LEADING = 126; + const T_LEFT = 127; + const T_LIKE = 128; + const T_MAX = 129; + const T_MEMBER = 130; + const T_MIN = 131; + const T_NOT = 132; + const T_NULL = 133; + const T_OF = 134; + const T_OR = 135; + const T_ORDER = 136; + const T_OUTER = 137; + const T_SELECT = 138; + const T_SET = 139; + const T_SIZE = 140; + const T_SOME = 141; + const T_SUM = 142; + const T_TRAILING = 143; + const T_TRUE = 144; + const T_UPDATE = 145; + const T_WHERE = 146; + const T_WITH = 147; + const T_PARTIAL = 148; + const T_MOD = 149; /** * Creates a new query scanner object. @@ -144,22 +139,26 @@ 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; + // Recognizing numeric values + if (is_numeric($value)) { return (strpos($value, '.') !== false || stripos($value, 'e') !== false) ? self::T_FLOAT : self::T_INTEGER; } - + + // Differentiate between quoted names, identifiers, input parameters and symbols if ($value[0] === "'") { $value = str_replace("''", "'", substr($value, 1, strlen($value) - 2)); - return self::T_STRING; } else if (ctype_alpha($value[0]) || $value[0] === '_') { - return $this->_checkLiteral($value); + $name = 'Doctrine\ORM\Query\Lexer::T_' . strtoupper($value); + if (defined($name)) { + $type = constant($name); + if ($type > 100) { + return $type; + } + } + return self::T_IDENTIFIER; } else if ($value[0] === '?' || $value[0] === ':') { return self::T_INPUT_PARAMETER; } else { @@ -186,41 +185,4 @@ class Lexer extends \Doctrine\Common\Lexer return $type; } - - /** - * @todo Inline this method. - */ - private function _getNumeric($value) - { - if ( ! is_scalar($value)) { - return false; - } - // Checking for valid numeric numbers: 1.234, -1.234e-2 - if (is_numeric($value)) { - return $value; - } - - return false; - } - - /** - * Checks if an identifier is a keyword and returns its correct type. - * - * @param string $identifier identifier name - * @return int token type - * @todo Inline this method. - */ - private function _checkLiteral($identifier) - { - $name = 'Doctrine\ORM\Query\Lexer::T_' . strtoupper($identifier); - - if (defined($name)) { - $type = constant($name); - if ($type > 100) { - return $type; - } - } - - return self::T_IDENTIFIER; - } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 241a28751..70ec16484 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -231,7 +231,11 @@ class Parser */ public function match($token) { - if ( ! ($this->_lexer->lookahead['type'] === $token)) { + // short-circuit on first condition, usually types match + if ($this->_lexer->lookahead['type'] !== $token && + $token !== Lexer::T_IDENTIFIER && + $this->_lexer->lookahead['type'] <= Lexer::T_IDENTIFIER + ) { $this->syntaxError($this->_lexer->getLiteral($token)); } @@ -890,7 +894,8 @@ class Parser $identVariable = $this->IdentificationVariable(); $this->match(Lexer::T_DOT); - $this->match($this->_lexer->lookahead['type']); + $this->match(Lexer::T_IDENTIFIER); + //$this->match($this->_lexer->lookahead['type']); $field = $this->_lexer->token['value']; // Validate association field diff --git a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php index 9fc1d6024..0518e2975 100644 --- a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php +++ b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php @@ -413,12 +413,23 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase { $this->assertValidDql('SELECT u, u.id + ?1 AS someNumber FROM Doctrine\Tests\Models\CMS\CmsUser u'); } - + + /** + * @group DDC-505 + */ public function testDQLKeywordInJoinIsAllowed() { $this->assertValidDql('SELECT u FROM ' . __NAMESPACE__ . '\DQLKeywordsModelUser u JOIN u.group g'); } + /** + * @group DDC-505 + */ + public function testDQLKeywordInConditionIsAllowed() + { + $this->assertValidDql('SELECT g FROM ' . __NAMESPACE__ . '\DQLKeywordsModelGroup g WHERE g.from=0'); + } + /* The exception is currently thrown in the SQLWalker, not earlier. public function testInverseSideSingleValuedAssociationPathNotAllowed() { @@ -441,4 +452,6 @@ class DQLKeywordsModelGroup { /** @Id @Column(type="integer") @GeneratedValue */ private $id; + /** @Column */ + private $from; }