Passed
Push — main ( 263507...cf95a9 )
by Thierry
01:46
created

ExportAdmin::dumpRoutines()   A

Complexity

Conditions 6
Paths 10

Size

Total Lines 25
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 18
c 0
b 0
f 0
nc 10
nop 1
dl 0
loc 25
rs 9.0444
1
<?php
2
3
namespace Lagdo\DbAdmin\DbAdmin;
4
5
use Exception;
6
7
use function count;
8
use function in_array;
9
use function preg_match;
10
use function str_replace;
11
use function array_unique;
12
use function array_merge;
13
use function array_pop;
14
15
/**
16
 * Admin export functions
17
 */
18
class ExportAdmin extends AbstractAdmin
19
{
20
    use Traits\TableExportTrait;
21
    use Traits\TableDumpTrait;
1 ignored issue
show
introduced by
The trait Lagdo\DbAdmin\DbAdmin\Traits\TableDumpTrait requires some properties which are not provided by Lagdo\DbAdmin\DbAdmin\ExportAdmin: $fullType, $type
Loading history...
22
23
    /**
24
     * The databases to dump
25
     *
26
     * @var array
27
     */
28
    private $databases;
29
30
    /**
31
     * The tables to dump
32
     *
33
     * @var array
34
     */
35
    private $tables;
36
37
    /**
38
     * Get data for export
39
     *
40
     * @param string $database      The database name
41
     * @param string $table
42
     *
43
     * @return array
44
     */
45
    public function getExportOptions(string $database, string $table = ''): array
46
    {
47
        $results = [
48
            'options' => $this->getBaseOptions($database, $table),
49
            'prefixes' => [],
50
            'labels' => [
51
                'export' => $this->trans->lang('Export'),
52
            ],
53
        ];
54
        if (($database)) {
55
            $results['tables'] = $this->getDbTables();
56
        } else {
57
            $results['databases'] = $this->getDatabases();
58
        }
59
        return $results;
60
    }
61
62
    /**
63
     * Dump routines in the connected database
64
     *
65
     * @param string $database      The database name
66
     *
67
     * @return void
68
     */
69
    private function dumpRoutines(string $database)
70
    {
71
        // From dump.inc.php
72
        $style = $this->options['db_style'];
73
74
        if ($this->options['routines']) {
75
            $sql = 'SHOW FUNCTION STATUS WHERE Db = ' . $this->driver->quote($database);
76
            foreach ($this->driver->rows($sql) as $row) {
77
                $sql = 'SHOW CREATE FUNCTION ' . $this->driver->escapeId($row['Name']);
78
                $create = $this->admin->removeDefiner($this->driver->result($sql, 2));
79
                $this->queries[] = $this->driver->setUtf8mb4($create);
80
                if ($style != 'DROP+CREATE') {
81
                    $this->queries[] = 'DROP FUNCTION IF EXISTS ' . $this->driver->escapeId($row['Name']) . ';;';
82
                }
83
                $this->queries[] = "$create;;\n";
84
            }
85
            $sql = 'SHOW PROCEDURE STATUS WHERE Db = ' . $this->driver->quote($database);
86
            foreach ($this->driver->rows($sql) as $row) {
87
                $sql = 'SHOW CREATE PROCEDURE ' . $this->driver->escapeId($row['Name']);
88
                $create = $this->admin->removeDefiner($this->driver->result($sql, 2));
89
                $this->queries[] = $this->driver->setUtf8mb4($create);
90
                if ($style != 'DROP+CREATE') {
91
                    $this->queries[] = 'DROP PROCEDURE IF EXISTS ' . $this->driver->escapeId($row['Name']) . ';;';
92
                }
93
                $this->queries[] = "$create;;\n";
94
            }
95
        }
96
    }
97
98
    /**
99
     * Dump events in the connected database
100
     *
101
     * @return void
102
     */
103
    private function dumpEvents()
104
    {
105
        // From dump.inc.php
106
        $style = $this->options['db_style'];
107
108
        if ($this->options['events']) {
109
            foreach ($this->driver->rows('SHOW EVENTS') as $row) {
110
                $sql = 'SHOW CREATE EVENT ' . $this->driver->escapeId($row['Name']);
111
                $create = $this->admin->removeDefiner($this->driver->result($sql, 3));
112
                $this->queries[] = $this->driver->setUtf8mb4($create);
113
                if ($style != 'DROP+CREATE') {
114
                    $this->queries[] = 'DROP EVENT IF EXISTS ' . $this->driver->escapeId($row['Name']) . ';;';
115
                }
116
                $this->queries[] = "$create;;\n";
117
            }
118
        }
119
    }
120
121
    /**
122
     * @param string $table
123
     * @param bool $dumpTable
124
     * @param bool $dumpData
125
     *
126
     * @return void
127
     */
128
    private function dumpTable(string $table, bool $dumpTable, bool $dumpData)
129
    {
130
        $this->dumpTableOrView($table, ($dumpTable ? $this->options['table_style'] : ''));
131
        if ($dumpData) {
132
            $fields = $this->driver->fields($table);
133
            $query = 'SELECT *' . $this->driver->convertFields($fields, $fields) .
134
                ' FROM ' . $this->driver->table($table);
135
            $this->dumpData($table, $query);
136
        }
137
        if ($this->options['is_sql'] && $this->options['triggers'] && $dumpTable &&
138
            ($triggers = $this->driver->sqlForCreateTrigger($table))) {
139
            $this->queries[] = 'DELIMITER ;';
140
            $this->queries[] = $triggers;
141
            $this->queries[] = 'DELIMITER ;';
142
        }
143
        if ($this->options['is_sql']) {
144
            $this->queries[] = '';
145
        }
146
    }
147
148
    /**
149
     * @param string $database      The database name
150
     *
151
     * @return void
152
     */
153
    private function dumpTables(string $database)
154
    {
155
        $dbDumpTable = $this->tables['list'] === '*' && in_array($database, $this->databases['list']);
156
        $dbDumpData = in_array($database, $this->databases['data']);
157
        $this->views = []; // View names
158
        $this->fkeys = []; // Table names for foreign keys
159
        $dbTables = $this->driver->tableStatuses(true);
160
        foreach ($dbTables as $table => $tableStatus) {
161
            $isView = $this->driver->isView($tableStatus);
162
            if ($isView) {
163
                // The views will be dumped after the tables
164
                $this->views[] = $table;
165
                continue;
166
            }
167
            $this->fkeys[] = $table;
168
            $dumpTable = $dbDumpTable || in_array($table, $this->tables['list']);
169
            $dumpData = $dbDumpData || in_array($table, $this->tables['data']);
170
            if ($dumpTable || $dumpData) {
171
                $this->dumpTable($table, $dumpTable, $dumpData);
172
            }
173
        }
174
    }
175
176
    /**
177
     * @return void
178
     */
179
    private function dumpViewsAndFKeys()
180
    {
181
        // Add FKs after creating tables (except in MySQL which uses SET FOREIGN_KEY_CHECKS=0)
182
        if ($this->driver->support('fkeys_sql')) {
183
            foreach ($this->fkeys as $table) {
184
                $this->queries[] = $this->driver->sqlForForeignKeys($table);
185
            }
186
        }
187
        // Dump the views after all the tables
188
        foreach ($this->views as $view) {
189
            $this->dumpTableOrView($view, $this->options['table_style'], 1);
190
        }
191
    }
192
193
    /**
194
     * @param string $database
195
     *
196
     * @return void
197
     */
198
    private function dumpDatabaseCreation(string $database)
199
    {
200
        $style = $this->options['db_style'];
201
        $this->driver->connect($database, '');
202
        $sql = 'SHOW CREATE DATABASE ' . $this->driver->escapeId($database);
203
        if ($this->options['is_sql'] && preg_match('~CREATE~', $style) &&
204
            ($create = $this->driver->result($sql, 1))) {
205
            $this->driver->setUtf8mb4($create);
206
            if ($style == 'DROP+CREATE') {
207
                $this->queries[] = 'DROP DATABASE IF EXISTS ' . $this->driver->escapeId($database) . ';';
208
            }
209
            $this->queries[] = $create . ';';
210
            $this->queries[] = ''; // Empty line
211
        }
212
        if ($this->options['is_sql'] && $this->options['db_style'] && $this->driver->jush() === 'sql') {
213
            if (($query = $this->driver->sqlForUseDatabase($database))) {
214
                $this->queries[] = $query . ';';
215
            }
216
            $this->queries[] = ''; // Empty line
217
        }
218
    }
219
220
    /**
221
     * @param string $database
222
     *
223
     * @return void
224
     */
225
    private function dumpDatabase(string $database)
226
    {
227
        $this->dumpDatabaseCreation($database);
228
        if ($this->options['is_sql'] && $this->driver->jush() === 'sql') {
229
            $count = count($this->queries);
230
            $this->queries[] = "DELIMITER ;;\n";
231
            // Dump routines and events currently works only for MySQL.
232
            $this->dumpRoutines($database);
233
            $this->dumpEvents();
234
            $this->queries[] = "DELIMITER ;;\n";
235
            if ($count + 2 === count($this->queries)) {
236
                // No routine or event were dumped, so the last 2 entries are removed.
237
                array_pop($this->queries);
238
                array_pop($this->queries);
239
            }
240
        }
241
242
        if (!$this->options['table_style'] && !$this->options['data_style']) {
243
            return;
244
        }
245
246
        $this->dumpTables($database);
247
        $this->dumpViewsAndFKeys();
248
    }
249
250
    /**
251
     * @return array|null
252
     */
253
    private function getDatabaseExportHeaders()
254
    {
255
        if (!$this->options['is_sql']) {
256
            return null;
257
        }
258
        $headers = [
259
            'version' => $this->driver->version(),
260
            'driver' => $this->driver->name(),
261
            'server' => str_replace("\n", ' ', $this->driver->serverInfo()),
262
            'sql' => false,
263
            'data_style' => false,
264
        ];
265
        if ($this->driver->jush() == 'sql') {
266
            $headers['sql'] = true;
267
            if (isset($options['data_style'])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $options seems to never exist and therefore isset should always be false.
Loading history...
268
                $headers['data_style'] = true;
269
            }
270
            // Set some options in database server
271
            $this->driver->query("SET time_zone = '+00:00'");
272
            $this->driver->query("SET sql_mode = ''");
273
        }
274
        return $headers;
275
    }
276
277
    /**
278
     * Export databases
279
     *
280
     * @param array  $databases     The databases to dump
281
     * @param array  $tables        The tables to dump
282
     * @param array  $options       The export options
283
     *
284
     * @return array|string
285
     */
286
    public function exportDatabases(array $databases, array $tables, array $options)
287
    {
288
        // From dump.inc.php
289
        // $tables = array_flip($options['tables']) + array_flip($options['data']);
290
        // $ext = dump_headers((count($tables) == 1 ? key($tables) : DB), (DB == '' || count($tables) > 1));
291
        $options['is_sql'] = preg_match('~sql~', $options['format']);
292
        $this->databases = $databases;
293
        $this->tables = $tables;
294
        $this->options = $options;
295
296
        $headers = $this->getDatabaseExportHeaders();
297
298
        foreach (array_unique(array_merge($databases['list'], $databases['data'])) as $database) {
299
            try {
300
                $this->dumpDatabase($database);
301
            }
302
            catch (Exception $e) {
303
                return $e->getMessage();
304
            }
305
        }
306
307
        if ($this->options['is_sql']) {
308
            $this->queries[] = '-- ' . $this->driver->result('SELECT NOW()');
309
        }
310
311
        return [
312
            'headers' => $headers,
313
            'queries' => $this->queries,
314
        ];
315
    }
316
}
317