Passed
Branch main (217991)
by Thierry
02:18
created

Table::alterTable()   F

Complexity

Conditions 14
Paths 1224

Size

Total Lines 33
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 14
eloc 24
c 1
b 0
f 0
nc 1224
nop 9
dl 0
loc 33
rs 2.1

How to fix   Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace Lagdo\DbAdmin\Driver\MySql\Db;
4
5
use Lagdo\DbAdmin\Driver\Entity\TableFieldEntity;
6
use Lagdo\DbAdmin\Driver\Entity\TableEntity;
7
use Lagdo\DbAdmin\Driver\Entity\IndexEntity;
8
use Lagdo\DbAdmin\Driver\Entity\ForeignKeyEntity;
9
use Lagdo\DbAdmin\Driver\Entity\TriggerEntity;
10
11
use Lagdo\DbAdmin\Driver\Db\ConnectionInterface;
12
13
use Lagdo\DbAdmin\Driver\Db\Table as AbstractTable;
14
15
class Table extends AbstractTable
16
{
17
    /**
18
     * @param bool $fast
19
     * @param string $table
20
     *
21
     * @return array
22
     */
23
    private function queryStatus(bool $fast, string $table = '')
24
    {
25
        $query = ($fast && $this->driver->minVersion(5)) ?
26
            "SELECT TABLE_NAME AS Name, ENGINE AS Engine, TABLE_COMMENT AS Comment " .
27
            "FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() " .
28
            ($table != "" ? "AND TABLE_NAME = " . $this->driver->quote($table) : "ORDER BY Name") :
29
            "SHOW TABLE STATUS" . ($table != "" ? " LIKE " . $this->driver->quote(addcslashes($table, "%_\\")) : "");
30
        return $this->driver->rows($query);
31
    }
32
33
    /**
34
     * @param array $row
35
     *
36
     * @return TableEntity
37
     */
38
    private function makeStatus(array $row)
39
    {
40
        $status = new TableEntity($row['Name']);
41
        $status->engine = $row['Engine'];
42
        if ($row["Engine"] == "InnoDB") {
43
            // ignore internal comment, unnecessary since MySQL 5.1.21
44
            $status->comment = preg_replace('~(?:(.+); )?InnoDB free: .*~', '\1', $row["Comment"]);
45
        }
46
        // if (!isset($row["Engine"])) {
47
        //     $row["Comment"] = "";
48
        // }
49
50
        return $status;
51
    }
52
53
    /**
54
     * @inheritDoc
55
     */
56
    public function tableStatus(string $table, bool $fast = false)
57
    {
58
        $rows = $this->queryStatus($fast, $table);
59
        if (!($row = reset($rows))) {
60
            return null;
61
        }
62
        return $this->makeStatus($row);
63
    }
64
65
    /**
66
     * @inheritDoc
67
     */
68
    public function tableStatuses(bool $fast = false)
69
    {
70
        $tables = [];
71
        $rows = $this->queryStatus($fast);
72
        foreach ($rows as $row) {
73
            $tables[$row["Name"]] = $this->makeStatus($row);
74
        }
75
        return $tables;
76
    }
77
78
    /**
79
     * @inheritDoc
80
     */
81
    public function tableNames()
82
    {
83
        $tables = [];
84
        $rows = $this->queryStatus(true);
85
        foreach ($rows as $row) {
86
            $tables[] = $row["Name"];
87
        }
88
        return $tables;
89
    }
90
91
    /**
92
     * @inheritDoc
93
     */
94
    public function fields(string $table)
95
    {
96
        $fields = [];
97
        foreach ($this->driver->rows("SHOW FULL COLUMNS FROM " . $this->driver->table($table)) as $row) {
98
            preg_match('~^([^( ]+)(?:\((.+)\))?( unsigned)?( zerofill)?$~', $row["Type"], $match);
99
            $matchCount = count($match);
100
            $match1 = $matchCount > 1 ? $match[1] : '';
101
            $match2 = $matchCount > 2 ? $match[2] : '';
102
            $match3 = $matchCount > 3 ? $match[3] : '';
103
            $match4 = $matchCount > 4 ? $match[4] : '';
104
105
            $field = new TableFieldEntity();
106
107
            $field->name = $row["Field"];
108
            $field->fullType = $row["Type"];
109
            $field->type = $match1;
110
            $field->length = intval($match2);
111
            $field->unsigned = ltrim($match3 . $match4);
112
            $field->default = ($row["Default"] != "" || preg_match("~char|set~", $match1) ?
113
                (preg_match('~text~', $match1) ? stripslashes(preg_replace("~^'(.*)'\$~", '\1',
114
                $row["Default"])) : $row["Default"]) : null);
115
            $field->null = ($row["Null"] == "YES");
116
            $field->autoIncrement = ($row["Extra"] == "auto_increment");
117
            $field->onUpdate = (preg_match('~^on update (.+)~i', $row["Extra"], $match) ? $match1 : ""); //! available since MySQL 5.1.23
118
            $field->collation = $row["Collation"];
119
            $field->privileges = array_flip(preg_split('~, *~', $row["Privileges"]));
120
            $field->comment = $row["Comment"];
121
            $field->primary = ($row["Key"] == "PRI");
122
            // https://mariadb.com/kb/en/library/show-columns/
123
            // https://github.com/vrana/adminer/pull/359#pullrequestreview-276677186
124
            $field->generated = preg_match('~^(VIRTUAL|PERSISTENT|STORED)~', $row["Extra"]) > 0;
125
126
            $fields[$field->name] = $field;
127
        }
128
        return $fields;
129
    }
130
131
    /**
132
     * @inheritDoc
133
     */
134
    public function supportForeignKeys(TableEntity $tableStatus)
135
    {
136
        return preg_match('~InnoDB|IBMDB2I~i', $tableStatus->engine)
137
            || (preg_match('~NDB~i', $tableStatus->engine) && $this->driver->minVersion(5.6));
138
    }
139
140
    /**
141
     * @inheritDoc
142
     */
143
    public function referencableTables(string $table)
144
    {
145
        $fields = []; // table_name => [field]
146
        foreach ($this->tableStatuses(true) as $tableName => $tableStatus) {
147
            if ($tableName === $table || !$this->supportForeignKeys($tableStatus)) {
148
                continue;
149
            }
150
            foreach ($this->fields($tableName) as $field) {
151
                if ($field->primary) {
152
                    if (!isset($fields[$tableName])) {
153
                        $fields[$tableName] = $field;
154
                    } else {
155
                        // No multi column primary key
156
                        $fields[$tableName] = null;
157
                    }
158
                }
159
            }
160
        }
161
        return array_filter($fields, function($field) {
162
            return $field !== null;
163
        });
164
    }
165
166
    /**
167
     * @inheritDoc
168
     */
169
    public function isView(TableEntity $tableStatus)
170
    {
171
        return $tableStatus->engine === null;
172
    }
173
174
    /**
175
     * @inheritDoc
176
     */
177
    public function indexes(string $table, ConnectionInterface $connection = null)
178
    {
179
        $indexes = [];
180
        foreach ($this->driver->rows("SHOW INDEX FROM " . $this->driver->table($table), $connection) as $row) {
181
            $index = new IndexEntity();
182
183
            $name = $row["Key_name"];
184
            $index->type = ($name == "PRIMARY" ? "PRIMARY" :
185
                ($row["Index_type"] == "FULLTEXT" ? "FULLTEXT" : ($row["Non_unique"] ?
186
                ($row["Index_type"] == "SPATIAL" ? "SPATIAL" : "INDEX") : "UNIQUE")));
187
            $index->columns[] = $row["Column_name"];
188
            $index->lengths[] = ($row["Index_type"] == "SPATIAL" ? null : $row["Sub_part"]);
189
            $index->descs[] = null;
190
191
            $indexes[$name] = $index;
192
        }
193
        return $indexes;
194
    }
195
196
    /**
197
     * @inheritDoc
198
     */
199
    public function foreignKeys(string $table)
200
    {
201
        static $pattern = '(?:`(?:[^`]|``)+`|"(?:[^"]|"")+")';
202
        $foreignKeys = [];
203
        $onActions = $this->driver->actions();
204
        $create_table = $this->connection->result("SHOW CREATE TABLE " . $this->driver->table($table), 1);
205
        if ($create_table) {
206
            preg_match_all("~CONSTRAINT ($pattern) FOREIGN KEY ?\\(((?:$pattern,? ?)+)\\) REFERENCES " .
207
                "($pattern)(?:\\.($pattern))? \\(((?:$pattern,? ?)+)\\)(?: ON DELETE ($onActions))" .
208
                "?(?: ON UPDATE ($onActions))?~", $create_table, $matches, PREG_SET_ORDER);
209
210
            foreach ($matches as $match) {
211
                $matchCount = count($match);
212
                $match1 = $matchCount > 1 ? $match[1] : '';
213
                $match2 = $matchCount > 2 ? $match[2] : '';
214
                $match3 = $matchCount > 3 ? $match[3] : '';
215
                $match4 = $matchCount > 4 ? $match[4] : '';
216
                $match5 = $matchCount > 5 ? $match[5] : '';
217
218
                preg_match_all("~$pattern~", $match2, $source);
219
                preg_match_all("~$pattern~", $match5, $target);
220
221
                $foreignKey = new ForeignKeyEntity();
222
223
                $foreignKey->database = $this->driver->unescapeId($match4 != "" ? $match3 : $match4);
224
                $foreignKey->table = $this->driver->unescapeId($match4 != "" ? $match4 : $match3);
225
                $foreignKey->source = array_map(function ($idf) {
226
                    return $this->driver->unescapeId($idf);
227
                }, $source[0]);
228
                $foreignKey->target = array_map(function ($idf) {
229
                    return $this->driver->unescapeId($idf);
230
                }, $target[0]);
231
                $foreignKey->onDelete = $matchCount > 6 ? $match[6] : "RESTRICT";
232
                $foreignKey->onUpdate = $matchCount > 7 ? $match[7] : "RESTRICT";
233
234
                $foreignKeys[$this->driver->unescapeId($match1)] = $foreignKey;
235
            }
236
        }
237
        return $foreignKeys;
238
    }
239
240
    /**
241
     * @inheritDoc
242
     */
243
    public function trigger(string $name, string $table = '')
244
    {
245
        if ($name == "") {
246
            return null;
247
        }
248
        $rows = $this->driver->rows("SHOW TRIGGERS WHERE `Trigger` = " . $this->driver->quote($name));
249
        if (!($row = reset($rows))) {
250
            return null;
251
        }
252
        return new TriggerEntity($row["Timing"], $row["Event"], '', '', $row["Trigger"]);
253
    }
254
255
    /**
256
     * @inheritDoc
257
     */
258
    public function triggers(string $table)
259
    {
260
        $triggers = [];
261
        foreach ($this->driver->rows("SHOW TRIGGERS LIKE " . $this->driver->quote(addcslashes($table, "%_\\"))) as $row) {
262
            $triggers[$row["Trigger"]] = new TriggerEntity($row["Timing"], $row["Event"], '', '', $row["Trigger"]);
263
        }
264
        return $triggers;
265
    }
266
267
    /**
268
     * @inheritDoc
269
     */
270
    public function triggerOptions()
271
    {
272
        return [
273
            "Timing" => ["BEFORE", "AFTER"],
274
            "Event" => ["INSERT", "UPDATE", "DELETE"],
275
            "Type" => ["FOR EACH ROW"],
276
        ];
277
    }
278
279
    /**
280
     * @inheritDoc
281
     */
282
    public function tableHelp(string $name)
283
    {
284
        $maria = preg_match('~MariaDB~', $this->connection->serverInfo());
285
        if ($this->driver->isInformationSchema($this->driver->database())) {
286
            return strtolower(($maria ? "information-schema-$name-table/" : str_replace("_", "-", $name) . "-table.html"));
287
        }
288
        if ($this->driver->database() == "mysql") {
289
            return ($maria ? "mysql$name-table/" : "system-database.html"); //! more precise link
290
        }
291
    }
292
}
293