MigrateHelper::createSchemaFromSqlfile()   F
last analyzed

Complexity

Conditions 28
Paths 4186

Size

Total Lines 124
Code Lines 80

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 28
eloc 80
c 1
b 0
f 1
nc 4186
nop 0
dl 0
loc 124
rs 0

How to fix   Long Method    Complexity   

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:

1
<?php declare(strict_types=1);
2
3
namespace XoopsModules\Publisher\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
        /** @var array $lines */
63
        $lines = \file($this->fileSql);
64
65
        // remove unnecessary lines
66
        foreach ($lines as $key => $value) {
67
            $line = \trim($value);
68
            // remove blank lines
69
            if ('' === $line) {
70
                unset($lines[$key]);
71
            }
72
            // remove comment lines
73
            if (0 === \strpos($line, '#')) {
74
                unset($lines[$key]);
75
            }
76
        }
77
78
        $skip      = true;
79
        $skipWords = ['CREATE DATABASE ', 'CREATE VIEW ', 'INSERT INTO ', 'SELECT ', 'DELETE ', 'UPDATE ', 'ALTER ', 'DROP '];
80
        $options   = '';
81
        // read remaining lines line by line and create new schema
82
        foreach ($lines as $key => $value) {
83
            $line = \trim($value);
84
            foreach ($skipWords as $skipWord) {
85
                if (0 === \stripos($line, $skipWord)) {
86
                    $skip = true;
87
                }
88
            }
89
            if (0 === \stripos($line, 'CREATE TABLE')) {
90
                $skip    = false;
91
                /** @var string $options */
92
                $options = '';
93
                // start table definition
94
                /** @var string $tableName */
95
                $tableName = $this->getTableName($line);
96
                $tables[$tableName]            = [];
97
                $tables[$tableName]['options'] = '';
98
                $tables[$tableName]['columns'] = [];
99
                $tables[$tableName]['keys']    = [];
100
            } elseif (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...
101
                if (0 === \stripos($line, ')')) {
102
                    // end of table definition
103
                    // get options
104
                    $this->getOptions($line, $options);
105
                    $tables[$tableName]['options'] = $options;
106
                } elseif (0 === \stripos($line, 'ENGINE')) {
107
                    $this->getOptions($line, $options);
108
                    $tables[$tableName]['options'] = $options;
109
                } elseif (0 === \stripos($line, 'DEFAULT CHARSET ')) {
110
                    $this->getOptions($line, $options);
111
                    $tables[$tableName]['options'] = $options;
112
                } else {
113
                    // get keys and fields
114
                    switch (\mb_strtoupper(\substr($line, 0, 3))) {
115
                        case 'KEY':
116
                        case 'PRI':
117
                        case 'UNI':
118
                            $tables[$tableName]['keys'][] = $this->getKey($line);
119
                            break;
120
                        case 'else':
121
                        default:
122
                            $columns                         = $this->getColumns($line);
123
                            $tables[$tableName]['columns'][] = $columns;
124
                            break;
125
                    }
126
                }
127
            }
128
129
        }
130
131
        // create array for new schema
132
        $level1 = \str_repeat(' ', 4);
133
        $level2 = \str_repeat(' ', 8);
134
        $level3 = \str_repeat(' ', 12);
135
136
        foreach ($tables as $tkey => $table) {
137
            $schema[] = "{$tkey}:\n";
138
            foreach ($table as $lkey => $line) {
139
                if ('keys' == $lkey) {
140
                    $schema[] = $level1 . "keys:\n";
141
                    foreach ($line as $kkey => $kvalue) {
142
                        foreach ($kvalue as $kkey2 => $kvalue2) {
143
                            $schema[] = $level2 . $kkey2 . ":\n";
144
                            $schema[] = $level3 . 'columns: ' . $kvalue2['columns'] . "\n";
145
                            $schema[] = $level3 . 'unique: ' . $kvalue2['unique'] . "\n";
146
                        }
147
                    }
148
                } elseif ('options' == $lkey) {
149
                    $schema[] = $level1 . 'options: ' . $line . "\n";
150
                } else {
151
                    $schema[] = $level1 . 'columns: ' . "\n";
152
                    foreach ($line as $kkey => $kvalue) {
153
                        $schema[] = $level2 . '-' . "\n";
154
                        foreach ($kvalue as $kkey2 => $kvalue2) {
155
                            $schema[] = $level3 . $kkey2 . ': ' . $kvalue2 . "\n";
156
                        }
157
                    }
158
                }
159
            }
160
        }
161
162
        // create new file and write schema array into this file
163
        $myfile = \fopen($this->fileYaml, 'wb');
164
        if (false == $myfile || null === $myfile) {
165
            \xoops_error('Error: Unable to open sql file!');
166
            return false;
167
        }
168
        foreach ($schema as $line) {
169
            \fwrite($myfile, $line);
170
        }
171
        \fclose($myfile);
172
173
        return true;
174
    }
175
176
    /**
177
     * Extract table name from given line
178
     *
179
     * @param string $line
180
     * @return string|bool
181
     */
182
    private function getTableName(string $line)
183
    {
184
        $arrLine = \explode('`', $line);
185
        if (\is_array($arrLine) && isset($arrLine[1])) {
186
            return $arrLine[1];
187
        }
188
189
        return false;
190
    }
191
192
    /**
193
     * Extract columns/fields of table from given line
194
     *
195
     * @param string $line
196
     * @return array|bool
197
     */
198
    private function getColumns(string $line)
199
    {
200
        $columns = [];
201
202
        /** @var array $arrCol */
203
        $arrCol = \explode(' ', \trim($line));
204
        if (\count($arrCol) > 0) {
205
            $name = \str_replace(['`'], '', $arrCol[0]);
206
        } else {
207
            return false;
208
        }
209
        $attributes = \trim(\str_replace([$name, '`', ','], '', $line));
210
211
        $columns['name'] = $name;
212
        // update quotes
213
        if (\strpos($attributes, "''") > 0) {
214
            $attributes = \trim(\str_replace("''", "''''''''", $attributes));
215
        } elseif (\strpos($attributes, "'") > 0) {
216
            $attributes = \trim(\str_replace("'", "''", $attributes));
217
        }
218
        $columns['attributes'] = "' " . $attributes . " '";
219
220
        return $columns;
221
    }
222
223
    /**
224
     * Extract options of table from given line
225
     *
226
     * @param string $line
227
     * @param string $options
228
     * @return void
229
     */
230
    private function getOptions(string $line, &$options): void
231
    {
232
        $lineText = \trim(\str_replace([')', ';'], '', $line));
233
        // remove all existing '
234
        $options = \str_replace("'", '', $options);
235
        if ('' != $options) {
236
            $options .= ' ';
237
        }
238
        $options = "'" . $options . $lineText . "'";
239
    }
240
241
    /**
242
     * Extract keys of table from given line
243
     *
244
     * @param string $line
245
     * @return array
246
     */
247
    private function getKey(string $line): array
248
    {
249
        $key = [];
250
251
        if (\strpos($line, 'RIMARY') > 0) {
252
            $key['PRIMARY']            = [];
253
            $fields                    = \substr($line, 13, \strlen($line) - 13);
254
            $key['PRIMARY']['columns'] = \str_replace(['`', '),', ')'], '', $fields);
255
            $key['PRIMARY']['unique']  = 'true';
256
        } else {
257
            $unique = 'false';
258
            if (\strpos($line, 'NIQUE') > 0) {
259
                $unique = 'true';
260
            }
261
            $line    = \trim(\str_replace(['UNIQUE KEY', 'KEY'], '', $line));
262
            /** @var array $arrName */
263
            $arrName = \explode('(', $line);
264
            if (\count($arrName) > 0) {
265
                /** @var string $name */
266
                $name    = \str_replace(['`', ' '], '', $arrName[0]);
267
                $columns = \str_replace(['`', '),', ')'], '', $arrName[1]);
268
                if ('' === $name) {
269
                    $name = $columns;
270
                }
271
                if (\strpos($name, ' ') > 0) {
272
                    $name = "'" . $name . "'";
273
                }
274
                $key[$name] = [];
275
                if (\strpos($columns, ' ') > 0) {
276
                    $columns = "'" . $columns . "'";
277
                }
278
                $key[$name]['columns'] = $columns;
279
                $key[$name]['unique']  = $unique;
280
            }
281
        }
282
        return $key;
283
    }
284
}
285