SqlParser\Utils\BufferedQuery::extract PHP Method

extract() public method

Extracts a statement from the buffer.
public extract ( boolean $end = false ) : string
$end boolean Whether the end of the buffer was reached.
return string
    public function extract($end = false)
    {
        /**
         * The last parsed position.
         *
         * This is statically defined because it is not used outside anywhere
         * outside this method and there is probably a (minor) performance
         * improvement to it.
         *
         * @var int
         */
        static $i = 0;
        if (empty($this->query)) {
            return false;
        }
        /**
         * The length of the buffer.
         *
         * @var int $len
         */
        $len = strlen($this->query);
        /**
         * The last index of the string that is going to be parsed.
         *
         * There must be a few characters left in the buffer so the parser can
         * avoid confusing some symbols that may have multiple meanings.
         *
         * For example, if the buffer ends in `-` that may be an operator or the
         * beginning of a comment.
         *
         * Another example if the buffer ends in `DELIMITE`. The parser is going
         * to require a few more characters because that may be a part of the
         * `DELIMITER` keyword or just a column named `DELIMITE`.
         *
         * Those extra characters are required only if there is more data
         * expected (the end of the buffer was not reached).
         *
         * @var int $loopLen
         */
        $loopLen = $end ? $len : $len - 16;
        for (; $i < $loopLen; ++$i) {
            /**
             * Handling backslash.
             *
             * Even if the next character is a special character that should be
             * treated differently, because of the preceding backslash, it will
             * be ignored.
             */
            if (($this->status & static::STATUS_COMMENT) == 0 && $this->query[$i] === '\\') {
                $this->current .= $this->query[$i] . $this->query[++$i];
                continue;
            }
            /*
             * Handling special parses statuses.
             */
            if ($this->status === static::STATUS_STRING_SINGLE_QUOTES) {
                // Single-quoted strings like 'foo'.
                if ($this->query[$i] === '\'') {
                    $this->status = 0;
                }
                $this->current .= $this->query[$i];
                continue;
            } elseif ($this->status === static::STATUS_STRING_DOUBLE_QUOTES) {
                // Double-quoted strings like "bar".
                if ($this->query[$i] === '"') {
                    $this->status = 0;
                }
                $this->current .= $this->query[$i];
                continue;
            } elseif ($this->status === static::STATUS_STRING_BACKTICK) {
                if ($this->query[$i] === '`') {
                    $this->status = 0;
                }
                $this->current .= $this->query[$i];
                continue;
            } elseif ($this->status === static::STATUS_COMMENT_BASH || $this->status === static::STATUS_COMMENT_SQL) {
                // Bash-like (#) or SQL-like (-- ) comments end in new line.
                if ($this->query[$i] === "\n") {
                    $this->status = 0;
                }
                continue;
            } elseif ($this->status === static::STATUS_COMMENT_C) {
                // C-like comments end in */.
                if ($this->query[$i - 1] === '*' && $this->query[$i] === '/') {
                    $this->status = 0;
                }
                continue;
            }
            /*
             * Checking if a string started.
             */
            if ($this->query[$i] === '\'') {
                $this->status = static::STATUS_STRING_SINGLE_QUOTES;
                $this->current .= $this->query[$i];
                continue;
            } elseif ($this->query[$i] === '"') {
                $this->status = static::STATUS_STRING_DOUBLE_QUOTES;
                $this->current .= $this->query[$i];
                continue;
            } elseif ($this->query[$i] === '`') {
                $this->status = static::STATUS_STRING_BACKTICK;
                $this->current .= $this->query[$i];
                continue;
            }
            /*
             * Checking if a comment started.
             */
            if ($this->query[$i] === '#') {
                $this->status = static::STATUS_COMMENT_BASH;
                continue;
            } elseif ($i + 2 < $len && $this->query[$i] === '-' && $this->query[$i + 1] === '-' && Context::isWhitespace($this->query[$i + 2])) {
                $this->status = static::STATUS_COMMENT_SQL;
                continue;
            } elseif ($i + 2 < $len && $this->query[$i] === '/' && $this->query[$i + 1] === '*' && $this->query[$i + 2] !== '!') {
                $this->status = static::STATUS_COMMENT_C;
                continue;
            }
            /*
             * Handling `DELIMITER` statement.
             *
             * The code below basically checks for
             *     `strtoupper(substr($this->query, $i, 9)) === 'DELIMITER'`
             *
             * This optimization makes the code about 3 times faster.
             *
             * `DELIMITER` is not being considered a keyword. The only context
             * it has a special meaning is when it is the beginning of a
             * statement. This is the reason for the last condition.
             */
            if ($i + 9 < $len && ($this->query[$i] === 'D' || $this->query[$i] === 'd') && ($this->query[$i + 1] === 'E' || $this->query[$i + 1] === 'e') && ($this->query[$i + 2] === 'L' || $this->query[$i + 2] === 'l') && ($this->query[$i + 3] === 'I' || $this->query[$i + 3] === 'i') && ($this->query[$i + 4] === 'M' || $this->query[$i + 4] === 'm') && ($this->query[$i + 5] === 'I' || $this->query[$i + 5] === 'i') && ($this->query[$i + 6] === 'T' || $this->query[$i + 6] === 't') && ($this->query[$i + 7] === 'E' || $this->query[$i + 7] === 'e') && ($this->query[$i + 8] === 'R' || $this->query[$i + 8] === 'r') && Context::isWhitespace($this->query[$i + 9]) && trim($this->current) === '') {
                // Saving the current index to be able to revert any parsing
                // done in this block.
                $iBak = $i;
                $i += 9;
                // Skipping `DELIMITER`.
                // Skipping whitespaces.
                while ($i < $len && Context::isWhitespace($this->query[$i])) {
                    ++$i;
                }
                // Parsing the delimiter.
                $delimiter = '';
                while ($i < $len && !Context::isWhitespace($this->query[$i])) {
                    $delimiter .= $this->query[$i++];
                }
                // Checking if the delimiter definition ended.
                if ($delimiter != '' && ($i < $len && Context::isWhitespace($this->query[$i]) || $i === $len && $end)) {
                    // Saving the delimiter.
                    $this->setDelimiter($delimiter);
                    // Whether this statement should be returned or not.
                    $ret = '';
                    if (!empty($this->options['parse_delimiter'])) {
                        // Appending the `DELIMITER` statement that was just
                        // found to the current statement.
                        $ret = trim($this->current . ' ' . substr($this->query, $iBak, $i - $iBak));
                    }
                    // Removing the statement that was just extracted from the
                    // query.
                    $this->query = substr($this->query, $i);
                    $i = 0;
                    // Resetting the current statement.
                    $this->current = '';
                    return $ret;
                }
                // Incomplete statement. Reverting
                $i = $iBak;
                return false;
            }
            /*
             * Checking if the current statement finished.
             *
             * The first letter of the delimiter is being checked as an
             * optimization. This code is almost as fast as the one above.
             *
             * There is no point in checking if two strings match if not even
             * the first letter matches.
             */
            if ($this->query[$i] === $this->delimiter[0] && ($this->delimiterLen === 1 || substr($this->query, $i, $this->delimiterLen) === $this->delimiter)) {
                // Saving the statement that just ended.
                $ret = $this->current;
                // If needed, adds a delimiter at the end of the statement.
                if (!empty($this->options['add_delimiter'])) {
                    $ret .= $this->delimiter;
                }
                // Removing the statement that was just extracted from the
                // query.
                $this->query = substr($this->query, $i + $this->delimiterLen);
                $i = 0;
                // Resetting the current statement.
                $this->current = '';
                // Returning the statement.
                return trim($ret);
            }
            /*
             * Appending current character to current statement.
             */
            $this->current .= $this->query[$i];
        }
        if ($end && $i === $len) {
            // If the end of the buffer was reached, the buffer is emptied and
            // the current statement that was extracted is returned.
            $ret = $this->current;
            // Emptying the buffer.
            $this->query = '';
            $i = 0;
            // Resetting the current statement.
            $this->current = '';
            // Returning the statement.
            return trim($ret);
        }
        return '';
    }

Usage Example

示例#1
0
 /**
  * @dataProvider testExtractProvider
  */
 public function testExtract($query, $chunkSize, array $options = array(), array $expected)
 {
     $chunks = str_split($query, $chunkSize);
     $count = count($chunks);
     /**
      * The array of extracted statements.
      *
      * @var array $statements
      */
     $statements = array();
     /**
      * The `BufferedQuery` instance used for extraction.
      *
      * @var BufferedQuery $bq
      */
     $bq = new BufferedQuery('', $options);
     // Feeding chunks and extracting queries.
     $i = 0;
     while ($i < $count) {
         if ($stmt = $bq->extract()) {
             $statements[] = $stmt;
         } else {
             $bq->query .= $chunks[$i++];
         }
     }
     // Feeding ended, extracting remaining queries.
     while ($stmt = $bq->extract(true)) {
         $statements[] = $stmt;
     }
     $this->assertEquals($expected, $statements);
 }
All Usage Examples Of SqlParser\Utils\BufferedQuery::extract