SqlParser\Utils\Formatter::formatList PHP Method

formatList() public method

Formats the given list of tokens.
public formatList ( TokensList $list ) : string
$list SqlParser\TokensList The list of tokens.
return string
    public function formatList($list)
    {
        /**
         * The query to be returned.
         *
         * @var string $ret
         */
        $ret = '';
        /**
         * The indentation level.
         *
         * @var int $indent
         */
        $indent = 0;
        /**
         * Whether the line ended.
         *
         * @var bool $lineEnded
         */
        $lineEnded = false;
        /**
         * Whether current group is short (no linebreaks)
         *
         * @var bool $shortGroup
         */
        $shortGroup = false;
        /**
         * The name of the last clause.
         *
         * @var string $lastClause
         */
        $lastClause = '';
        /**
         * A stack that keeps track of the indentation level every time a new
         * block is found.
         *
         * @var array $blocksIndentation
         */
        $blocksIndentation = array();
        /**
         * A stack that keeps track of the line endings every time a new block
         * is found.
         *
         * @var array $blocksLineEndings
         */
        $blocksLineEndings = array();
        /**
         * Whether clause's options were formatted.
         *
         * @var bool $formattedOptions
         */
        $formattedOptions = false;
        /**
         * Previously parsed token.
         *
         * @var Token $prev
         */
        $prev = null;
        /**
         * Comments are being formatted separately to maintain the whitespaces
         * before and after them.
         *
         * @var string $comment
         */
        $comment = '';
        // In order to be able to format the queries correctly, the next token
        // must be taken into consideration. The loop below uses two pointers,
        // `$prev` and `$curr` which store two consecutive tokens.
        // Actually, at every iteration the previous token is being used.
        for ($list->idx = 0; $list->idx < $list->count; ++$list->idx) {
            /**
             * Token parsed at this moment.
             *
             * @var Token $curr
             */
            $curr = $list->tokens[$list->idx];
            if ($curr->type === Token::TYPE_WHITESPACE) {
                // Whitespaces are skipped because the formatter adds its own.
                continue;
            } elseif ($curr->type === Token::TYPE_COMMENT) {
                // Whether the comments should be parsed.
                if (!empty($this->options['remove_comments'])) {
                    continue;
                }
                if ($list->tokens[$list->idx - 1]->type === Token::TYPE_WHITESPACE) {
                    // The whitespaces before and after are preserved for
                    // formatting reasons.
                    $comment .= $list->tokens[$list->idx - 1]->token;
                }
                $comment .= $this->toString($curr);
                if ($list->tokens[$list->idx + 1]->type === Token::TYPE_WHITESPACE && $list->tokens[$list->idx + 2]->type !== Token::TYPE_COMMENT) {
                    // Adding the next whitespace only there is no comment that
                    // follows it immediately which may cause adding a
                    // whitespace twice.
                    $comment .= $list->tokens[$list->idx + 1]->token;
                }
                // Everything was handled here, no need to continue.
                continue;
            }
            // Checking if pointers were initialized.
            if ($prev !== null) {
                // Checking if a new clause started.
                if (static::isClause($prev) !== false) {
                    $lastClause = $prev->value;
                    $formattedOptions = false;
                }
                // The options of a clause should stay on the same line and everything that follows.
                if ($this->options['parts_newline'] && !$formattedOptions && empty(self::$INLINE_CLAUSES[$lastClause]) && ($curr->type !== Token::TYPE_KEYWORD || $curr->type === Token::TYPE_KEYWORD && $curr->flags & Token::FLAG_KEYWORD_FUNCTION)) {
                    $formattedOptions = true;
                    $lineEnded = true;
                    ++$indent;
                }
                // Checking if this clause ended.
                if ($tmp = static::isClause($curr)) {
                    if ($tmp == 2 || $this->options['clause_newline']) {
                        $lineEnded = true;
                        if ($this->options['parts_newline']) {
                            --$indent;
                        }
                    }
                }
                // Indenting BEGIN ... END blocks.
                if ($prev->type === Token::TYPE_KEYWORD && $prev->value === 'BEGIN') {
                    $lineEnded = true;
                    array_push($blocksIndentation, $indent);
                    ++$indent;
                } elseif ($curr->type === Token::TYPE_KEYWORD && $curr->value === 'END') {
                    $lineEnded = true;
                    $indent = array_pop($blocksIndentation);
                }
                // Formatting fragments delimited by comma.
                if ($prev->type === Token::TYPE_OPERATOR && $prev->value === ',') {
                    // Fragments delimited by a comma are broken into multiple
                    // pieces only if the clause is not inlined or this fragment
                    // is between brackets that are on new line.
                    if (empty(self::$INLINE_CLAUSES[$lastClause]) && !$shortGroup && $this->options['parts_newline'] || end($blocksLineEndings) === true) {
                        $lineEnded = true;
                    }
                }
                // Handling brackets.
                // Brackets are indented only if the length of the fragment between
                // them is longer than 30 characters.
                if ($prev->type === Token::TYPE_OPERATOR && $prev->value === '(') {
                    array_push($blocksIndentation, $indent);
                    $shortGroup = true;
                    if (static::getGroupLength($list) > 30) {
                        ++$indent;
                        $lineEnded = true;
                        $shortGroup = false;
                    }
                    array_push($blocksLineEndings, $lineEnded);
                } elseif ($curr->type === Token::TYPE_OPERATOR && $curr->value === ')') {
                    $indent = array_pop($blocksIndentation);
                    $lineEnded |= array_pop($blocksLineEndings);
                    $shortGroup = false;
                }
                // Delimiter must be placed on the same line with the last
                // clause.
                if ($curr->type === Token::TYPE_DELIMITER) {
                    $lineEnded = false;
                }
                // Adding the token.
                $ret .= $this->toString($prev);
                // Finishing the line.
                if ($lineEnded) {
                    if ($indent < 0) {
                        // TODO: Make sure this never occurs and delete it.
                        $indent = 0;
                    }
                    if ($curr->type !== Token::TYPE_COMMENT) {
                        $ret .= $this->options['line_ending'] . str_repeat($this->options['indentation'], $indent);
                    }
                    $lineEnded = false;
                } else {
                    // If the line ended there is no point in adding whitespaces.
                    // Also, some tokens do not have spaces before or after them.
                    if (!($prev->type === Token::TYPE_OPERATOR && ($prev->value === '.' || $prev->value === '(') || $curr->type === Token::TYPE_OPERATOR && ($curr->value === '.' || $curr->value === ',' || $curr->value === '(' || $curr->value === ')') || $curr->type === Token::TYPE_DELIMITER && mb_strlen($curr->value, 'UTF-8') < 2) || $prev->value === 'DELIMITER') {
                        $ret .= ' ';
                    }
                }
            }
            if (!empty($comment)) {
                $ret .= $comment;
                $comment = '';
            }
            // Iteration finished, consider current token as previous.
            $prev = $curr;
        }
        if ($this->options['type'] === 'cli') {
            return $ret . "";
        }
        return $ret;
    }

Usage Example

コード例 #1
0
ファイル: Formatter.php プロジェクト: flash1452/phpmyadmin
 /**
  * Formats a query.
  *
  * @param string $query   The query to be formatted
  * @param array  $options The formatting options.
  *
  * @return string          The formatted string.
  */
 public static function format($query, array $options = array())
 {
     $lexer = new Lexer($query);
     $formatter = new Formatter($options);
     return $formatter->formatList($lexer->list);
 }