diff --git a/src/Parser/AbstractParser.php b/src/Parser/AbstractParser.php index 4aab18ab..aef98f20 100644 --- a/src/Parser/AbstractParser.php +++ b/src/Parser/AbstractParser.php @@ -138,7 +138,7 @@ private function storeQuery(State $state, &$queries) */ private function isStatementEmpty($statement) { - return mb_ereg_match('^\\s*$', $statement); + return trim($statement) === ''; } /** @@ -244,8 +244,15 @@ protected function handleNumberedParameter(State $state) */ protected function handleSemiColon(State $state) { - $uselessCharacters = $state->capture(';\\s*'); - $state->passString($uselessCharacters); + while (! $state->done()) + { + $character = $state->getCurrentCharacter(); + if (! in_array($character, array(';', "\r", "\n", "\t", " "), true)) + { + break; + } + $state->passString($character); + } $state->setNewStatementCharacterFound(true); } diff --git a/src/Parser/PgsqlParser.php b/src/Parser/PgsqlParser.php index 92fee167..47bd1a75 100644 --- a/src/Parser/PgsqlParser.php +++ b/src/Parser/PgsqlParser.php @@ -91,10 +91,22 @@ protected function handlePossibleCStyleString(State $state) } if (! $inCString) { // Checking if we have blank characters until next quote. In which case it is the same string - $blanks = $state->capture("\\s*'"); + $offset = 1; + $blanks = true; + while (! $state->done()) { + $characterAtOffset = $state->getCharacterFromCurrent($offset); + if ($characterAtOffset === "'") { + break; + } + if (! in_array($characterAtOffset, array(" ", "\n", "\r", "\t"), true)) { + $blanks = false; + break; + } + $offset ++; + } if ($blanks) { - $state->copyUntilCharacter("'"); $state->copyCurrentCharacter(); + $state->copyUntilCharacter("'"); $inCString = true; } } @@ -111,7 +123,17 @@ protected function handlePossibleCStyleString(State $state) */ protected function handleDollar(State $state) { - $identifier = $state->capture('\\$([a-zA-Z_]\\w*)*\\$'); + $identifier = '$'; + $offset = 1; + while (! $state->done()) { + $character = $state->getCharacterFromCurrent($offset); + $identifier .= $character; + if ($character === '$') { + break; + } + $offset ++; + } + if ($identifier) { // Copy until the end of the starting tag $state->copyUntilCharacter($identifier); diff --git a/src/Parser/State.php b/src/Parser/State.php index 7f841ea5..ae240df4 100644 --- a/src/Parser/State.php +++ b/src/Parser/State.php @@ -69,6 +69,8 @@ class State */ protected $new_statement_character_found = false; + protected $valid_placeholder_characters = []; + /** * * Constructor @@ -90,6 +92,12 @@ public function __construct($statement, $values = array(), $charset = 'UTF-8') if (array_key_exists(0, $this->values)) { array_unshift($this->values, null); } + $this->valid_placeholder_characters = array_merge( + range('a', 'z'), + range ('A', 'Z'), + range (0, 9), + array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_') + ); } /** @@ -162,6 +170,16 @@ public function getCurrentCharacter() return mb_substr($this->statement, $this->current_index, 1, $this->charset); } + /** + * Returns the character $n position after the current character + * @param $n + * @return string + */ + public function getCharacterFromCurrent($n) + { + return mb_substr($this->statement, $this->current_index + $n, 1, $this->charset); + } + /** * * Returns the modified SQL query @@ -321,31 +339,20 @@ public function getCharset() */ public function getIdentifier() { - return $this->capture('\\w+\\b'); - } - - /** - * - * Tries to matche a regular expression starting at current index and returns the result - * - * @param string $regexp - * @param int $capture_group - * @return string - */ - public function capture($regexp, $capture_group = 0) - { - $capture = ''; - if ($this->last_index <= $this->current_index) { - return $capture; - } - mb_regex_encoding($this->charset); - if (mb_ereg_search_init($this->statement) !== false) { - mb_ereg_search_setpos($this->current_index); - if ($matches = mb_ereg_search_regs('\\G' . $regexp)) { - $capture = isset($matches[$capture_group]) ? $matches[$capture_group] : ''; + $identifier = ''; + $length = 0; + while (! $this->done()) + { + $character = mb_substr($this->statement, $this->current_index + $length, 1, $this->charset); + if (! in_array($character, $this->valid_placeholder_characters, true)) + { + return $identifier; } + $identifier .= $character; + $length++; + } - return $capture; + return $identifier; } /** diff --git a/tests/Parser/PgsqlParserTest.php b/tests/Parser/PgsqlParserTest.php index d81902a4..6498b8b4 100644 --- a/tests/Parser/PgsqlParserTest.php +++ b/tests/Parser/PgsqlParserTest.php @@ -175,7 +175,7 @@ public function testCStyleStringConstants() $sql = <<parseSingleQuery($sql, $parameters); $this->assertEquals($sql, $parsedQuery->getStatement()); @@ -206,6 +206,14 @@ public function testDollarQuotedStrings() $sql = 'SELECT $outer$ nested strings $inner$:foo$inner$ $outer$'; $parsedQuery = $this->parseSingleQuery($sql, $parameters); $this->assertEquals($sql, $parsedQuery->getStatement()); + + $sql = 'SELECT $€$hello$€$'; + $parsedQuery = $this->parseSingleQuery($sql, $parameters); + $this->assertEquals($sql, $parsedQuery->getStatement()); + + $sql = 'SELECT $€$hello$€'; + $parsedQuery = $this->parseSingleQuery($sql, $parameters); + $this->assertEquals($sql, $parsedQuery->getStatement()); } public function testTypeCasting()