Formatter::appendToFormattedSql()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 1
Metric Value
c 1
b 1
f 1
dl 0
loc 6
rs 9.4286
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
/**
3
 * Author: Nil Portugués Calderó <[email protected]>
4
 * Date: 6/26/14
5
 * Time: 12:10 AM.
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace NilPortugues\Sql\QueryFormatter;
12
13
use NilPortugues\Sql\QueryFormatter\Helper\Comment;
14
use NilPortugues\Sql\QueryFormatter\Helper\Indent;
15
use NilPortugues\Sql\QueryFormatter\Helper\NewLine;
16
use NilPortugues\Sql\QueryFormatter\Helper\Parentheses;
17
use NilPortugues\Sql\QueryFormatter\Helper\Token;
18
use NilPortugues\Sql\QueryFormatter\Helper\WhiteSpace;
19
use NilPortugues\Sql\QueryFormatter\Tokenizer\Tokenizer;
20
21
/**
22
 * Lightweight Formatter heavily based on https://github.com/jdorn/sql-formatter.
23
 *
24
 * Class Formatter
25
 */
26
class Formatter
27
{
28
    /**
29
     * @var Tokenizer
30
     */
31
    protected $tokenizer;
32
33
    /**
34
     * @var NewLine
35
     */
36
    protected $newLine;
37
38
    /**
39
     * @var Parentheses
40
     */
41
    protected $parentheses;
42
43
    /**
44
     * @var string
45
     */
46
    protected $tab = '    ';
47
    /**
48
     * @var int
49
     */
50
    protected $inlineCount = 0;
51
52
    /**
53
     * @var bool
54
     */
55
    protected $clauseLimit = false;
56
    /**
57
     * @var string
58
     */
59
    protected $formattedSql = '';
60
    /**
61
     * @var Indent
62
     */
63
    protected $indentation;
64
65
    /**
66
     * @var Comment
67
     */
68
    protected $comment;
69
70
    /**
71
     * Returns a SQL string in a readable human-friendly format.
72
     *
73
     * @param string $sql
74
     *
75
     * @return string
76
     */
77
    public function format($sql)
78
    {
79
        $this->reset();
80
        $tab = "\t";
81
82
        $originalTokens = $this->tokenizer->tokenize((string) $sql);
83
        $tokens = WhiteSpace::removeTokenWhitespace($originalTokens);
84
85
        foreach ($tokens as $i => $token) {
86
            $queryValue = $token[Tokenizer::TOKEN_VALUE];
87
            $this->indentation->increaseSpecialIndent()->increaseBlockIndent();
88
            $addedNewline = $this->newLine->addNewLineBreak($tab);
89
90
            if ($this->comment->stringHasCommentToken($token)) {
91
                $this->formattedSql = $this->comment->writeCommentBlock($token, $tab, $queryValue);
92
                continue;
93
            }
94
95
            if ($this->parentheses->getInlineParentheses()) {
96
                if ($this->parentheses->stringIsClosingParentheses($token)) {
97
                    $this->parentheses->writeInlineParenthesesBlock($tab, $queryValue);
98
                    continue;
99
                }
100
                $this->newLine->writeNewLineForLongCommaInlineValues($token);
101
                $this->inlineCount += \strlen($token[Tokenizer::TOKEN_VALUE]);
102
            }
103
104
            switch ($token) {
105
                case $this->parentheses->stringIsOpeningParentheses($token):
106
                    $tokens = $this->formatOpeningParenthesis($token, $i, $tokens, $originalTokens);
107
                    break;
108
109
                case $this->parentheses->stringIsClosingParentheses($token):
110
                    $this->indentation->decreaseIndentLevelUntilIndentTypeIsSpecial($this);
111
                    $this->newLine->addNewLineBeforeToken($addedNewline, $tab);
112
                    break;
113
114
                case $this->stringIsEndOfLimitClause($token):
115
                    $this->clauseLimit = false;
116
                    break;
117
118
                case $token[Tokenizer::TOKEN_VALUE] === ',' && false === $this->parentheses->getInlineParentheses():
119
                    $this->newLine->writeNewLineBecauseOfComma();
120
                    break;
121
122
                case Token::isTokenTypeReservedTopLevel($token):
123
                    $queryValue = $this->formatTokenTypeReservedTopLevel($addedNewline, $tab, $token, $queryValue);
124
                    break;
125
126
                case $this->newLine->isTokenTypeReservedNewLine($token):
127
                    $this->newLine->addNewLineBeforeToken($addedNewline, $tab);
128
129
                    if (WhiteSpace::tokenHasExtraWhiteSpaces($token)) {
130
                        $queryValue = \preg_replace('/\s+/', ' ', $queryValue);
131
                    }
132
                    break;
133
            }
134
135
            $this->formatBoundaryCharacterToken($token, $i, $tokens, $originalTokens);
136
            $this->formatWhiteSpaceToken($token, $queryValue);
137
            $this->formatDashToken($token, $i, $tokens);
138
        }
139
140
        return \trim(\str_replace(["\t", " \n"], [$this->tab, "\n"], $this->formattedSql))."\n";
141
    }
142
143
    /**
144
     *
145
     */
146
    public function reset()
147
    {
148
        $this->tokenizer = new Tokenizer();
149
        $this->indentation = new Indent();
150
        $this->parentheses = new Parentheses($this, $this->indentation);
151
        $this->newLine = new NewLine($this, $this->indentation, $this->parentheses);
152
        $this->comment = new Comment($this, $this->indentation, $this->newLine);
153
154
        $this->formattedSql = '';
155
    }
156
157
    /**
158
     * @param       $token
159
     * @param       $i
160
     * @param array $tokens
161
     * @param array $originalTokens
162
     *
163
     * @return array
164
     */
165
    protected function formatOpeningParenthesis($token, $i, array &$tokens, array &$originalTokens)
166
    {
167
        $length = 0;
168
        for ($j = 1; $j <= 250; ++$j) {
169
            if (isset($tokens[$i + $j])) {
170
                $next = $tokens[$i + $j];
171
                if ($this->parentheses->stringIsClosingParentheses($next)) {
172
                    $this->parentheses->writeNewInlineParentheses();
173
                    break;
174
                }
175
176
                if ($this->parentheses->invalidParenthesesTokenValue($next)
177
                    || $this->parentheses->invalidParenthesesTokenType($next)
178
                ) {
179
                    break;
180
                }
181
182
                $length += \strlen($next[Tokenizer::TOKEN_VALUE]);
183
            }
184
        }
185
        $this->newLine->writeNewLineForLongInlineValues($length);
186
187
        if (WhiteSpace::isPrecedingCurrentTokenOfTokenTypeWhiteSpace($originalTokens, $token)) {
188
            $this->formattedSql = \rtrim($this->formattedSql, ' ');
189
        }
190
191
        $this->newLine->addNewLineAfterOpeningParentheses();
192
193
        return $tokens;
194
    }
195
196
    /**
197
     * @param $token
198
     *
199
     * @return bool
200
     */
201
    protected function stringIsEndOfLimitClause($token)
202
    {
203
        return $this->clauseLimit
204
        && $token[Tokenizer::TOKEN_VALUE] !== ','
205
        && $token[Tokenizer::TOKEN_TYPE] !== Tokenizer::TOKEN_TYPE_NUMBER
206
        && $token[Tokenizer::TOKEN_TYPE] !== Tokenizer::TOKEN_TYPE_WHITESPACE;
207
    }
208
209
    /**
210
     * @param bool   $addedNewline
211
     * @param string $tab
212
     * @param $token
213
     * @param $queryValue
214
     *
215
     * @return mixed
216
     */
217
    protected function formatTokenTypeReservedTopLevel($addedNewline, $tab, $token, $queryValue)
218
    {
219
        $this->indentation
220
            ->setIncreaseSpecialIndent(true)
221
            ->decreaseSpecialIndentIfCurrentIndentTypeIsSpecial();
222
223
        $this->newLine->writeNewLineBecauseOfTopLevelReservedWord($addedNewline, $tab);
224
225
        if (WhiteSpace::tokenHasExtraWhiteSpaces($token)) {
226
            $queryValue = \preg_replace('/\s+/', ' ', $queryValue);
227
        }
228
        Token::tokenHasLimitClause($token, $this->parentheses, $this);
229
230
        return $queryValue;
231
    }
232
233
    /**
234
     * @param       $token
235
     * @param       $i
236
     * @param array $tokens
237
     * @param array $originalTokens
238
     */
239
    protected function formatBoundaryCharacterToken($token, $i, array &$tokens, array &$originalTokens)
240
    {
241
        if (Token::tokenHasMultipleBoundaryCharactersTogether($token, $tokens, $i, $originalTokens)) {
242
            $this->formattedSql = \rtrim($this->formattedSql, ' ');
243
        }
244
    }
245
246
    /**
247
     * @param $token
248
     * @param $queryValue
249
     */
250
    protected function formatWhiteSpaceToken($token, $queryValue)
251
    {
252
        if (WhiteSpace::tokenHasExtraWhiteSpaceLeft($token)) {
253
            $this->formattedSql = \rtrim($this->formattedSql, ' ');
254
        }
255
256
        $this->formattedSql .= $queryValue.' ';
257
258
        if (WhiteSpace::tokenHasExtraWhiteSpaceRight($token)) {
259
            $this->formattedSql = \rtrim($this->formattedSql, ' ');
260
        }
261
    }
262
263
    /**
264
     * @param       $token
265
     * @param       $i
266
     * @param array $tokens
267
     */
268
    protected function formatDashToken($token, $i, array &$tokens)
269
    {
270
        if (Token::tokenIsMinusSign($token, $tokens, $i)) {
271
            $previousTokenType = $tokens[$i - 1][Tokenizer::TOKEN_TYPE];
272
273
            if (WhiteSpace::tokenIsNumberAndHasExtraWhiteSpaceRight($previousTokenType)) {
274
                $this->formattedSql = \rtrim($this->formattedSql, ' ');
275
            }
276
        }
277
    }
278
279
    /**
280
     * @return string
281
     */
282
    public function getFormattedSql()
283
    {
284
        return $this->formattedSql;
285
    }
286
287
    /**
288
     * @param string $formattedSql
289
     *
290
     * @return $this
291
     */
292
    public function setFormattedSql($formattedSql)
293
    {
294
        $this->formattedSql = $formattedSql;
295
296
        return $this;
297
    }
298
299
    /**
300
     * @param $string
301
     *
302
     * @return $this
303
     */
304
    public function appendToFormattedSql($string)
305
    {
306
        $this->formattedSql .= $string;
307
308
        return $this;
309
    }
310
311
    /**
312
     * @return int
313
     */
314
    public function getInlineCount()
315
    {
316
        return $this->inlineCount;
317
    }
318
319
    /**
320
     * @param int $inlineCount
321
     *
322
     * @return $this
323
     */
324
    public function setInlineCount($inlineCount)
325
    {
326
        $this->inlineCount = $inlineCount;
327
328
        return $this;
329
    }
330
331
    /**
332
     * @return bool
333
     */
334
    public function getClauseLimit()
335
    {
336
        return $this->clauseLimit;
337
    }
338
339
    /**
340
     * @param bool $clauseLimit
341
     *
342
     * @return $this
343
     */
344
    public function setClauseLimit($clauseLimit)
345
    {
346
        $this->clauseLimit = $clauseLimit;
347
348
        return $this;
349
    }
350
}
351