MigrateHelper::getKey()   B
last analyzed

Complexity

Conditions 7
Paths 19

Size

Total Lines 34
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 25
c 1
b 0
f 0
nc 19
nop 1
dl 0
loc 34
rs 8.5866
1
<?php declare(strict_types=1);
2
3
namespace XoopsModules\Xhelp\Common;
4
5
/*
6
 You may not change or alter any portion of this comment or credits
7
 of supporting developers from this source code or any supporting source code
8
 which is considered copyrighted (c) material of the original comment or credit authors.
9
10
 This program is distributed in the hope that it will be useful,
11
 but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
 */
14
15
/**
16
 * Class to compare current DB table structure with sql/mysql.sql
17
 *
18
 * @category  Migrate Helper
19
 * @author    Goffy <[email protected]>
20
 * @copyright 2021 XOOPS Project (https://xoops.org)
21
 * @license   GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
22
 * @link      https://xoops.org
23
 */
24
class MigrateHelper
25
{
26
    /**
27
     * @var string
28
     */
29
    private $fileYaml;
30
    /**
31
     * @var string
32
     */
33
    private $fileSql;
34
35
    /**
36
     * @param string $fileSql
37
     * @param string $fileYaml
38
     */
39
    public function __construct(string $fileSql, string $fileYaml)
40
    {
41
        $this->fileSql  = $fileSql;
42
        $this->fileYaml = $fileYaml;
43
    }
44
45
    /**
46
     * Create a yaml file based on a sql file
47
     *
48
     * @return bool
49
     */
50
    public function createSchemaFromSqlfile(): bool
51
    {
52
        if (!\is_file($this->fileSql)) {
53
            \xoops_error('Error: Sql file not found!');
54
            return false;
55
        }
56
57
        $tables    = [];
58
        $schema    = [];
59
        $tableName = '';
60
61
        // read sql file
62
        $lines = \file($this->fileSql);
63
64
        // remove unnecessary lines
65
        foreach ($lines as $key => $value) {
66
            $line = \trim($value);
67
            // remove blank lines
68
            if ('' === $line) {
69
                unset($lines[$key]);
70
            }
71
            // remove comment lines
72
            if (0 === \strpos($line, '#')) {
73
                unset($lines[$key]);
74
            }
75
        }
76
77
        $skip      = true;
78
        $skipWords = ['CREATE DATABASE ', 'CREATE VIEW ', 'INSERT INTO ', 'SELECT ', 'DELETE ', 'UPDATE ', 'ALTER ', 'DROP '];
79
        $options   = '';
80
        // read remaining lines line by line and create new schema
81
        foreach ($lines as $key => $value) {
82
            $line = \trim($value);
83
            foreach ($skipWords as $skipWord) {
84
                if (0 === \stripos($line, $skipWord)) {
85
                    $skip = true;
86
                }
87
            }
88
            if (0 === \stripos($line, 'CREATE TABLE')) {
89
                $skip    = false;
90
                $options = '';
91
                // start table definition
92
                $tableName                     = $this->getTableName($line);
93
                $tables[$tableName]            = [];
94
                $tables[$tableName]['options'] = '';
95
                $tables[$tableName]['columns'] = [];
96
                $tables[$tableName]['keys']    = [];
97
            } else {
98
                if (false == $skip) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
99
                    if (0 === \stripos($line, ')')) {
100
                        // end of table definition
101
                        // get options
102
                        $this->getOptions($line, $options);
103
                        $tables[$tableName]['options'] = $options;
104
                    } elseif (0 === \stripos($line, 'ENGINE')) {
105
                        $this->getOptions($line, $options);
106
                        $tables[$tableName]['options'] = $options;
107
                    } elseif (0 === \stripos($line, 'DEFAULT CHARSET ')) {
108
                        $this->getOptions($line, $options);
109
                        $tables[$tableName]['options'] = $options;
110
                    } else {
111
                        // get keys and fields
112
                        switch (\mb_strtoupper(\substr($line, 0, 3))) {
113
                            case 'KEY':
114
                            case 'PRI':
115
                            case 'UNI':
116
                                $tables[$tableName]['keys'][] = $this->getKey($line);
117
                                break;
118
                            case 'else':
119
                            default:
120
                                $columns                         = $this->getColumns($line);
121
                                $tables[$tableName]['columns'][] = $columns;
122
                                break;
123
                        }
124
                    }
125
                }
126
            }
127
        }
128
129
        // create array for new schema
130
        $level1 = \str_repeat(' ', 4);
131
        $level2 = \str_repeat(' ', 8);
132
        $level3 = \str_repeat(' ', 12);
133
134
        foreach ($tables as $tkey => $table) {
135
            $schema[] = "{$tkey}:\n";
136
            foreach ($table as $lkey => $line) {
137
                if ('keys' === $lkey) {
138
                    $schema[] = $level1 . "keys:\n";
139
                    foreach ($line as $kkey => $kvalue) {
140
                        foreach ($kvalue as $kkey2 => $kvalue2) {
141
                            $schema[] = $level2 . $kkey2 . ":\n";
142
                            $schema[] = $level3 . 'columns: ' . $kvalue2['columns'] . "\n";
143
                            $schema[] = $level3 . 'unique: ' . $kvalue2['unique'] . "\n";
144
                        }
145
                    }
146
                } elseif ('options' === $lkey) {
147
                    $schema[] = $level1 . 'options: ' . $line . "\n";
148
                } else {
149
                    $schema[] = $level1 . 'columns: ' . "\n";
150
                    foreach ($line as $kkey => $kvalue) {
151
                        $schema[] = $level2 . '-' . "\n";
152
                        foreach ($kvalue as $kkey2 => $kvalue2) {
153
                            $schema[] = $level3 . $kkey2 . ': ' . $kvalue2 . "\n";
154
                        }
155
                    }
156
                }
157
            }
158
        }
159
160
        // create new file and write schema array into this file
161
        $myfile = \fopen($this->fileYaml, 'wb');
162
        if (false == $myfile || null === $myfile) {
163
            \xoops_error('Error: Unable to open sql file!');
164
            return false;
165
        }
166
        foreach ($schema as $line) {
167
            \fwrite($myfile, $line);
168
        }
169
        \fclose($myfile);
170
171
        return true;
172
    }
173
174
    /**
175
     * Extract table name from given line
176
     *
177
     * @param string $line
178
     * @return string|bool
179
     */
180
    private function getTableName(string $line)
181
    {
182
        $arrLine = \explode('`', $line);
183
        if (\count($arrLine) > 0) {
184
            return $arrLine[1];
185
        }
186
187
        return false;
188
    }
189
190
    /**
191
     * Extract columns/fields of table from given line
192
     *
193
     * @param string $line
194
     * @return array|bool
195
     */
196
    private function getColumns(string $line)
197
    {
198
        $columns = [];
199
200
        $arrCol = \explode(' ', \trim($line));
201
        if (\count($arrCol) > 0) {
202
            $name = \str_replace(['`'], '', $arrCol[0]);
203
        } else {
204
            return false;
205
        }
206
        $attributes = \trim(\str_replace([$name, '`', ','], '', $line));
207
208
        $columns['name'] = $name;
209
        // update quotes
210
        if (\strpos($attributes, "''") > 0) {
211
            $attributes = \trim(\str_replace("''", "''''''''", $attributes));
212
        } elseif (\strpos($attributes, "'") > 0) {
213
            $attributes = \trim(\str_replace("'", "''", $attributes));
214
        }
215
        $columns['attributes'] = "' " . $attributes . " '";
216
217
        return $columns;
218
    }
219
220
    /**
221
     * Extract options of table from given line
222
     *
223
     * @param string $line
224
     * @param string $options
225
     * @return void
226
     */
227
    private function getOptions(string $line, string &$options): void
228
    {
229
        $lineText = \trim(\str_replace([')', ';'], '', $line));
230
        // remove all existing '
231
        $options = \str_replace("'", '', $options);
232
        if ('' != $options) {
233
            $options .= ' ';
234
        }
235
        $options = "'" . $options . $lineText . "'";
236
    }
237
238
    /**
239
     * Extract keys of table from given line
240
     *
241
     * @param string $line
242
     * @return array
243
     */
244
    private function getKey(string $line): array
245
    {
246
        $key = [];
247
248
        if (\strpos($line, 'RIMARY') > 0) {
249
            $key['PRIMARY']            = [];
250
            $fields                    = \substr($line, 13, \strlen($line) - 13);
251
            $key['PRIMARY']['columns'] = \str_replace(['`', '),', ')'], '', $fields);
252
            $key['PRIMARY']['unique']  = 'true';
253
        } else {
254
            $unique = 'false';
255
            if (\strpos($line, 'NIQUE') > 0) {
256
                $unique = 'true';
257
            }
258
            $line    = \trim(\str_replace(['UNIQUE KEY', 'KEY'], '', $line));
259
            $arrName = \explode('(', $line);
260
            if (\count($arrName) > 0) {
261
                $name    = \str_replace(['`', ' '], '', $arrName[0]);
262
                $columns = \str_replace(['`', '),', ')'], '', $arrName[1]);
263
                if ('' === $name) {
264
                    $name = $columns;
265
                }
266
                if (\strpos($name, ' ') > 0) {
267
                    $name = "'" . $name . "'";
268
                }
269
                $key[$name] = [];
270
                if (\strpos($columns, ' ') > 0) {
271
                    $columns = "'" . $columns . "'";
272
                }
273
                $key[$name]['columns'] = $columns;
274
                $key[$name]['unique']  = $unique;
275
            }
276
        }
277
        return $key;
278
    }
279
}
280