AlterStatement::build()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 0
dl 0
loc 11
ccs 8
cts 8
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PhpMyAdmin\SqlParser\Statements;
6
7
use PhpMyAdmin\SqlParser\Components\AlterOperation;
8
use PhpMyAdmin\SqlParser\Components\Expression;
9
use PhpMyAdmin\SqlParser\Parser;
10
use PhpMyAdmin\SqlParser\Parsers\AlterOperations;
11
use PhpMyAdmin\SqlParser\Parsers\Expressions;
12
use PhpMyAdmin\SqlParser\Parsers\OptionsArrays;
13
use PhpMyAdmin\SqlParser\Statement;
14
use PhpMyAdmin\SqlParser\TokensList;
15
use PhpMyAdmin\SqlParser\TokenType;
16
17
use function implode;
18
use function trim;
19
20
/**
21
 * `ALTER` statement.
22
 */
23
class AlterStatement extends Statement
24
{
25
    /**
26
     * Table affected.
27
     */
28
    public Expression|null $table = null;
29
30
    /**
31
     * Column affected by this statement.
32
     *
33
     * @var AlterOperation[]|null
34
     */
35
    public array|null $altered = [];
36
37
    /**
38
     * Options of this statement.
39
     *
40
     * @var array<string, int|array<int, int|string>>
41
     * @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
42
     */
43
    public static array $statementOptions = [
44
        'ONLINE' => 1,
45
        'OFFLINE' => 1,
46
        'IGNORE' => 2,
47
        // `DEFINER` is also used for `ALTER EVENT`
48
        'DEFINER' => [
49
            2,
50
            'expr=',
51
        ],
52
        'DATABASE' => 3,
53
        'EVENT' => 3,
54
        'FUNCTION' => 3,
55
        'PROCEDURE' => 3,
56
        'SERVER' => 3,
57
        'TABLE' => 3,
58
        'TABLESPACE' => 3,
59
        'USER' => 3,
60
        'VIEW' => 3,
61
    ];
62
63
    /**
64
     * @param Parser     $parser the instance that requests parsing
65
     * @param TokensList $list   the list of tokens to be parsed
66
     */
67 242
    public function parse(Parser $parser, TokensList $list): void
68
    {
69 242
        ++$list->idx; // Skipping `ALTER`.
70 242
        $parsedOptions = OptionsArrays::parse($parser, $list, static::$statementOptions);
71 242
        if ($parsedOptions->isEmpty()) {
72 4
            $parser->error('Unrecognized alter operation.', $list->tokens[$list->idx]);
73
74 4
            return;
75
        }
76
77 238
        $this->options = $parsedOptions;
78 238
        ++$list->idx;
79
80
        // Parsing affected table.
81 238
        $this->table = Expressions::parse(
82 238
            $parser,
83 238
            $list,
84 238
            [
85 238
                'parseField' => 'table',
86 238
                'breakOnAlias' => true,
87 238
            ],
88 238
        );
89 238
        ++$list->idx; // Skipping field.
90
91
        /**
92
         * The state of the parser.
93
         *
94
         * Below are the states of the parser.
95
         *
96
         *      0 -----------------[ alter operation ]-----------------> 1
97
         *
98
         *      1 -------------------------[ , ]-----------------------> 0
99
         */
100 238
        $state = 0;
101
102 238
        for (; $list->idx < $list->count; ++$list->idx) {
103
            /**
104
             * Token parsed at this moment.
105
             */
106 238
            $token = $list->tokens[$list->idx];
107
108
            // End of statement.
109 238
            if ($token->type === TokenType::Delimiter) {
110 236
                break;
111
            }
112
113
            // Skipping whitespaces and comments.
114 236
            if (($token->type === TokenType::Whitespace) || ($token->type === TokenType::Comment)) {
115 46
                continue;
116
            }
117
118 236
            if ($state === 0) {
119 236
                $options = [];
120 236
                if ($this->options->has('DATABASE')) {
121 2
                    $options = AlterOperations::DATABASE_OPTIONS;
122 234
                } elseif ($this->options->has('TABLE')) {
123 118
                    $options = AlterOperations::TABLE_OPTIONS;
124 116
                } elseif ($this->options->has('VIEW')) {
125 2
                    $options = AlterOperations::VIEW_OPTIONS;
126 114
                } elseif ($this->options->has('USER')) {
127 22
                    $options = AlterOperations::USER_OPTIONS;
128 92
                } elseif ($this->options->has('EVENT')) {
129 44
                    $options = AlterOperations::EVENT_OPTIONS;
130 48
                } elseif ($this->options->has('FUNCTION') || $this->options->has('PROCEDURE')) {
131 48
                    $options = AlterOperations::ROUTINE_OPTIONS;
132
                }
133
134 236
                $this->altered[] = AlterOperations::parse($parser, $list, $options);
135 236
                $state = 1;
136 44
            } elseif ($state === 1) {
137 44
                if (($token->type === TokenType::Operator) && ($token->value === ',')) {
138 36
                    $state = 0;
139
                }
140
            }
141
        }
142
    }
143
144 48
    public function build(): string
145
    {
146 48
        $tmp = [];
147 48
        foreach ($this->altered as $altered) {
148 48
            $tmp[] = $altered->build();
149
        }
150
151 48
        return trim(
152 48
            'ALTER ' . $this->options->build()
0 ignored issues
show
Bug introduced by
The method build() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

152
            'ALTER ' . $this->options->/** @scrutinizer ignore-call */ build()

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
153 48
            . ' ' . $this->table->build()
0 ignored issues
show
Bug introduced by
The method build() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

153
            . ' ' . $this->table->/** @scrutinizer ignore-call */ build()

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
154 48
            . ' ' . implode(', ', $tmp),
155 48
        );
156
    }
157
}
158