diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index dad26168..147d86f8 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1052,7 +1052,7 @@ parameters: - message: "#^Parameter \\#1 \\$str of static method PhpMyAdmin\\\\SqlParser\\\\Context\\:\\:isSeparator\\(\\) expects string, mixed given\\.$#" - count: 4 + count: 7 path: src/Lexer.php - diff --git a/psalm-baseline.xml b/psalm-baseline.xml index c87d9b3d..2c9351da 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -695,21 +695,14 @@ $this->last $this->last - - $this->str[$this->last + 1] - $this->str[$this->last++] - $this->str[$this->last] - $this->str[$this->last] - $this->str[$this->last] - $this->str[$this->last] - $this->str[$this->last] - $this->str[$this->last] - $this->str[$this->last] - $this->str[$this->last] - $this->str[$this->last] + $this->str[$this->last] $this->str[$this->last] $this->str[$this->last] + + + $this->str[$this->last + 1] + $this->str[$this->last++] $this->str[$this->last] $this->str[$this->last] $this->str[$this->last] diff --git a/src/Lexer.php b/src/Lexer.php index 0e34bc38..a5063dbe 100644 --- a/src/Lexer.php +++ b/src/Lexer.php @@ -842,7 +842,7 @@ public function parseNumber() // or "E" causing wrongly interpreted scientific notation (".e[0 to 9]" is invalid). Such invalid notation could // break the lexer when table names under a given database context starts with ".e[0-9]". // - // Valid final states are: 2, 3, 4 and 6. Any parsing that finished in a + // Valid final states are: 2, 3, 4, 6, and 9. Any parsing that finished in a // state other than these is invalid. // Also, negative states are invalid states. $iBak = $this->last; @@ -886,14 +886,12 @@ public function parseNumber() $state = 4; } elseif ($this->str[$this->last] === 'e' || $this->str[$this->last] === 'E') { $state = 5; - } elseif ( - ($this->str[$this->last] >= 'a' && $this->str[$this->last] <= 'z') - || ($this->str[$this->last] >= 'A' && $this->str[$this->last] <= 'Z') - ) { - // A number can't be directly followed by a letter - $state = -$state; - break; } elseif ($this->str[$this->last] < '0' || $this->str[$this->last] > '9') { + if (! Context::isSeparator($this->str[$this->last])) { + // A number can't be directly followed by a letter _ or $ + $state = -$state; + } + // Just digits and `.`, `e` and `E` are valid characters. break; } @@ -901,14 +899,12 @@ public function parseNumber() $flags |= Token::FLAG_NUMBER_FLOAT; if ($this->str[$this->last] === 'e' || $this->str[$this->last] === 'E') { $state = 5; - } elseif ( - ($this->str[$this->last] >= 'a' && $this->str[$this->last] <= 'z') - || ($this->str[$this->last] >= 'A' && $this->str[$this->last] <= 'Z') - ) { - // A number can't be directly followed by a letter - $state = -$state; - break; } elseif ($this->str[$this->last] < '0' || $this->str[$this->last] > '9') { + if (! Context::isSeparator($this->str[$this->last])) { + // A number can't be directly followed by a letter _ or $ + $state = -$state; + } + // Just digits, `e` and `E` are valid characters. break; } @@ -919,14 +915,12 @@ public function parseNumber() || ($this->str[$this->last] >= '0' && $this->str[$this->last] <= '9') ) { $state = 6; - } elseif ( - ($this->str[$this->last] >= 'a' && $this->str[$this->last] <= 'z') - || ($this->str[$this->last] >= 'A' && $this->str[$this->last] <= 'Z') - ) { - // A number can't be directly followed by a letter - $state = -$state; - break; } else { + if (! Context::isSeparator($this->str[$this->last])) { + // A number can't be directly followed by a letter _ or $ + $state = -$state; + } + break; } } elseif ($state === 6) { diff --git a/tests/Lexer/LexerTest.php b/tests/Lexer/LexerTest.php index e310c94f..02eddf62 100644 --- a/tests/Lexer/LexerTest.php +++ b/tests/Lexer/LexerTest.php @@ -76,6 +76,7 @@ public static function lexProvider(): array ['lexer/lexKeyword'], ['lexer/lexKeyword2'], ['lexer/lexNumber'], + ['lexer/lexNumberAtStartOfName'], ['lexer/lexOperator'], ['lexer/lexOperatorStarIsArithmetic'], ['lexer/lexOperatorStarIsWildcard'], diff --git a/tests/data/lexer/lexNumberAtStartOfName.in b/tests/data/lexer/lexNumberAtStartOfName.in new file mode 100644 index 00000000..f068ab6c --- /dev/null +++ b/tests/data/lexer/lexNumberAtStartOfName.in @@ -0,0 +1 @@ +INSERT INTO 2_bar SELECT 1; \ No newline at end of file diff --git a/tests/data/lexer/lexNumberAtStartOfName.out b/tests/data/lexer/lexNumberAtStartOfName.out new file mode 100644 index 00000000..29e6cd3d --- /dev/null +++ b/tests/data/lexer/lexNumberAtStartOfName.out @@ -0,0 +1,124 @@ +{ + "query": "INSERT INTO 2_bar SELECT 1;", + "lexer": { + "@type": "PhpMyAdmin\\SqlParser\\Lexer", + "str": "INSERT INTO 2_bar SELECT 1;", + "len": 27, + "last": 27, + "list": { + "@type": "PhpMyAdmin\\SqlParser\\TokensList", + "tokens": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "INSERT", + "value": "INSERT", + "keyword": "INSERT", + "type": 1, + "flags": 35, + "position": 0 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 6 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "INTO", + "value": "INTO", + "keyword": "INTO", + "type": 1, + "flags": 3, + "position": 7 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 11 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "2_bar", + "value": "2_bar", + "keyword": null, + "type": 0, + "flags": 0, + "position": 12 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 17 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "SELECT", + "value": "SELECT", + "keyword": "SELECT", + "type": 1, + "flags": 3, + "position": 18 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 24 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "1", + "value": 1, + "keyword": null, + "type": 6, + "flags": 0, + "position": 25 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": ";", + "value": ";", + "keyword": null, + "type": 9, + "flags": 0, + "position": 26 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": null, + "value": null, + "keyword": null, + "type": 9, + "flags": 0, + "position": null + } + ], + "count": 11, + "idx": 0 + }, + "delimiter": ";", + "delimiterLen": 1, + "strict": false, + "errors": [] + }, + "parser": null, + "errors": { + "lexer": [], + "parser": [] + } +} \ No newline at end of file