Passed
Push — main ( acbf0f...7dd0c5 )
by Thierry
01:56
created

ExportAdmin::dumpViewsAndFKeys()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
c 0
b 0
f 0
nc 4
nop 0
dl 0
loc 11
rs 10
1
<?php
2
3
namespace Lagdo\DbAdmin\DbAdmin;
4
5
use Exception;
6
7
use function count;
8
use function compact;
9
use function preg_replace;
10
use function preg_match;
11
use function str_replace;
12
use function array_unique;
13
use function array_merge;
14
15
/**
16
 * Admin export functions
17
 */
18
class ExportAdmin extends AbstractAdmin
19
{
20
    use Traits\DbDumpTrait;
0 ignored issues
show
introduced by
The trait Lagdo\DbAdmin\DbAdmin\Traits\DbDumpTrait requires some properties which are not provided by Lagdo\DbAdmin\DbAdmin\ExportAdmin: $fullType, $type
Loading history...
21
22
    /**
23
     * The databases to dump
24
     *
25
     * @var array
26
     */
27
    private $databases;
28
29
    /**
30
     * The tables to dump
31
     *
32
     * @var array
33
     */
34
    private $tables;
35
36
    /**
37
     * @param string $database
38
     * @param string $table
39
     *
40
     * @return array
41
     */
42
    private function getBaseOptions(string $database, string $table): array
43
    {
44
        // From dump.inc.php
45
        $db_style = ['', 'USE', 'DROP+CREATE', 'CREATE'];
46
        $table_style = ['', 'DROP+CREATE', 'CREATE'];
47
        $data_style = ['', 'TRUNCATE+INSERT', 'INSERT'];
48
        if ($this->driver->jush() == 'sql') { //! use insertOrUpdate() in all drivers
49
            $data_style[] = 'INSERT+UPDATE';
50
        }
51
        // \parse_str($_COOKIE['adminer_export'], $row);
52
        // if(!$row) {
53
        $row = [
54
            'output' => 'text',
55
            'format' => 'sql',
56
            'db_style' => ($database != '' ? '' : 'CREATE'),
57
            'table_style' => 'DROP+CREATE',
58
            'data_style' => 'INSERT',
59
        ];
60
        // }
61
        // if(!isset($row['events'])) { // backwards compatibility
62
        $row['routines'] = $row['events'] = ($table == '');
63
        $row['triggers'] = $row['table_style'];
64
        // }
65
66
        $options = [
67
            'output' => [
68
                'label' => $this->trans->lang('Output'),
69
                'options' => $this->util->dumpOutput(),
70
                'value' => $row['output'],
71
            ],
72
            'format' => [
73
                'label' => $this->trans->lang('Format'),
74
                'options' => $this->util->dumpFormat(),
75
                'value' => $row['format'],
76
            ],
77
            'table_style' => [
78
                'label' => $this->trans->lang('Tables'),
79
                'options' => $table_style,
80
                'value' => $row['table_style'],
81
            ],
82
            'auto_increment' => [
83
                'label' => $this->trans->lang('Auto Increment'),
84
                'value' => 1,
85
                'checked' => $row['autoIncrement'] ?? false,
86
            ],
87
            'data_style' => [
88
                'label' => $this->trans->lang('Data'),
89
                'options' => $data_style,
90
                'value' => $row['data_style'],
91
            ],
92
        ];
93
        if ($this->driver->jush() !== 'sqlite') {
94
            $options['db_style'] = [
95
                'label' => $this->trans->lang('Database'),
96
                'options' => $db_style,
97
                'value' => $row['db_style'],
98
            ];
99
            if ($this->driver->support('routine')) {
100
                $options['routines'] = [
101
                    'label' => $this->trans->lang('Routines'),
102
                    'value' => 1,
103
                    'checked' => $row['routines'],
104
                ];
105
            }
106
            if ($this->driver->support('event')) {
107
                $options['events'] = [
108
                    'label' => $this->trans->lang('Events'),
109
                    'value' => 1,
110
                    'checked' => $row['events'],
111
                ];
112
            }
113
        }
114
        if ($this->driver->support('trigger')) {
115
            $options['triggers'] = [
116
                'label' => $this->trans->lang('Triggers'),
117
                'value' => 1,
118
                'checked' => $row['triggers'],
119
            ];
120
        }
121
        return $options;
122
    }
123
124
    /**
125
     * @return array
126
     */
127
    private function getDbTables(): array
128
    {
129
        $tables = [
130
            'headers' => [$this->trans->lang('Tables'), $this->trans->lang('Data')],
131
            'details' => [],
132
        ];
133
        $tables_list = $this->driver->tables();
134
        foreach ($tables_list as $name => $type) {
135
            $prefix = preg_replace('~_.*~', '', $name);
136
            //! % may be part of table name
137
            // $checked = ($TABLE == '' || $TABLE == (\substr($TABLE, -1) == '%' ? "$prefix%" : $name));
138
            // $results['prefixes'][$prefix]++;
139
140
            $tables['details'][] = compact('prefix', 'name', 'type'/*, 'checked'*/);
141
        }
142
        return $tables;
143
    }
144
145
    /**
146
     * @return array
147
     */
148
    private function getDatabases(): array
149
    {
150
        $databases = [
151
            'headers' => [$this->trans->lang('Database'), $this->trans->lang('Data')],
152
            'details' => [],
153
        ];
154
        $databases_list = $this->driver->databases(false) ?? [];
155
        foreach ($databases_list as $name) {
156
            if (!$this->driver->isInformationSchema($name)) {
157
                $prefix = preg_replace('~_.*~', '', $name);
158
                // $results['prefixes'][$prefix]++;
159
160
                $databases['details'][] = compact('prefix', 'name');
161
            }
162
        }
163
        return $databases;
164
    }
165
166
    /**
167
     * Get data for export
168
     *
169
     * @param string $database      The database name
170
     * @param string $table
171
     *
172
     * @return array
173
     */
174
    public function getExportOptions(string $database, string $table = ''): array
175
    {
176
        $results = [
177
            'options' => $this->getBaseOptions($database, $table),
178
            'prefixes' => [],
179
            'labels' => [
180
                'export' => $this->trans->lang('Export'),
181
            ],
182
        ];
183
        if (($database)) {
184
            $results['tables'] = $this->getDbTables();
185
        } else {
186
            $results['databases'] = $this->getDatabases();
187
        }
188
        return $results;
189
    }
190
191
    /**
192
     * Dump routines and events in the connected database
193
     *
194
     * @param string $database      The database name
195
     *
196
     * @return void
197
     */
198
    private function dumpRoutinesAndEvents(string $database)
199
    {
200
        // From dump.inc.php
201
        $style = $this->options['db_style'];
202
        $queries = [];
203
204
        if ($this->options['routines']) {
205
            $sql = 'SHOW FUNCTION STATUS WHERE Db = ' . $this->driver->quote($database);
206
            foreach ($this->driver->rows($sql) as $row) {
207
                $sql = 'SHOW CREATE FUNCTION ' . $this->driver->escapeId($row['Name']);
208
                $create = $this->admin->removeDefiner($this->driver->result($sql, 2));
209
                $queries[] = $this->driver->setUtf8mb4($create);
210
                if ($style != 'DROP+CREATE') {
211
                    $queries[] = 'DROP FUNCTION IF EXISTS ' . $this->driver->escapeId($row['Name']) . ';;';
212
                }
213
                $queries[] = "$create;;\n";
214
            }
215
            $sql = 'SHOW PROCEDURE STATUS WHERE Db = ' . $this->driver->quote($database);
216
            foreach ($this->driver->rows($sql) as $row) {
217
                $sql = 'SHOW CREATE PROCEDURE ' . $this->driver->escapeId($row['Name']);
218
                $create = $this->admin->removeDefiner($this->driver->result($sql, 2));
219
                $queries[] = $this->driver->setUtf8mb4($create);
220
                if ($style != 'DROP+CREATE') {
221
                    $queries[] = 'DROP PROCEDURE IF EXISTS ' . $this->driver->escapeId($row['Name']) . ';;';
222
                }
223
                $queries[] = "$create;;\n";
224
            }
225
        }
226
227
        if ($this->options['events']) {
228
            foreach ($this->driver->rows('SHOW EVENTS') as $row) {
229
                $sql = 'SHOW CREATE EVENT ' . $this->driver->escapeId($row['Name']);
230
                $create = $this->admin->removeDefiner($this->driver->result($sql, 3));
231
                $queries[] = $this->driver->setUtf8mb4($create);
232
                if ($style != 'DROP+CREATE') {
233
                    $queries[] = 'DROP EVENT IF EXISTS ' . $this->driver->escapeId($row['Name']) . ';;';
234
                }
235
                $queries[] = "$create;;\n";
236
            }
237
        }
238
239
        if (count($queries) > 0) {
240
            $this->queries[] = "DELIMITER ;;\n";
241
            foreach ($queries as $query) {
242
                $this->queries[] = $query;
243
            }
244
            $this->queries[] = "DELIMITER ;;\n";
245
        }
246
    }
247
248
    /**
249
     * @param string $database
250
     *
251
     * @return void
252
     */
253
    private function dumpDatabaseCreation(string $database)
254
    {
255
        $style = $this->options['db_style'];
256
        $this->driver->connect($database, '');
257
        $sql = 'SHOW CREATE DATABASE ' . $this->driver->escapeId($database);
258
        if ($this->options['is_sql'] && preg_match('~CREATE~', $style) &&
259
            ($create = $this->driver->result($sql, 1))) {
260
            $this->driver->setUtf8mb4($create);
261
            if ($style == 'DROP+CREATE') {
262
                $this->queries[] = 'DROP DATABASE IF EXISTS ' . $this->driver->escapeId($database) . ';';
263
            }
264
            $this->queries[] = $create . ";\n";
265
        }
266
    }
267
268
    /**
269
     * @param string $database
270
     *
271
     * @return void
272
     */
273
    private function dumpDatabase(string $database)
274
    {
275
        $this->dumpDatabaseCreation($database);
276
        if ($this->options['is_sql'] && $this->driver->jush() === 'sql') {
277
            // Dump routines and events currently works only for MySQL.
278
            if ($this->options['db_style']) {
279
                if (($query = $this->driver->sqlForUseDatabase($database))) {
280
                    $this->queries[] = $query . ';';
281
                }
282
                $this->queries[] = ''; // Empty line
283
            }
284
            $this->dumpRoutinesAndEvents($database);
285
        }
286
287
        if (!$this->options['table_style'] && !$this->options['data_style']) {
288
            return;
289
        }
290
291
        $this->dumpTables($database);
292
        $this->dumpViewsAndFKeys();
293
    }
294
295
    /**
296
     * Export databases
297
     *
298
     * @param array  $databases     The databases to dump
299
     * @param array  $tables        The tables to dump
300
     * @param array  $options       The export options
301
     *
302
     * @return array|string
303
     */
304
    public function exportDatabases(array $databases, array $tables, array $options)
305
    {
306
        // From dump.inc.php
307
        // $tables = array_flip($options['tables']) + array_flip($options['data']);
308
        // $ext = dump_headers((count($tables) == 1 ? key($tables) : DB), (DB == '' || count($tables) > 1));
309
        $options['is_sql'] = preg_match('~sql~', $options['format']);
310
        $this->databases = $databases;
311
        $this->tables = $tables;
312
        $this->options = $options;
313
314
        $headers = null;
315
        if ($this->options['is_sql']) {
316
            $headers = [
317
                'version' => $this->driver->version(),
318
                'driver' => $this->driver->name(),
319
                'server' => str_replace("\n", ' ', $this->driver->serverInfo()),
320
                'sql' => false,
321
                'data_style' => false,
322
            ];
323
            if ($this->driver->jush() == 'sql') {
324
                $headers['sql'] = true;
325
                if (isset($options['data_style'])) {
326
                    $headers['data_style'] = true;
327
                }
328
                // Set some options in database server
329
                $this->driver->query("SET time_zone = '+00:00'");
330
                $this->driver->query("SET sql_mode = ''");
331
            }
332
        }
333
334
        foreach (array_unique(array_merge($databases['list'], $databases['data'])) as $database) {
335
            try {
336
                $this->dumpDatabase($database);
337
            }
338
            catch (Exception $e) {
339
                return $e->getMessage();
340
            }
341
        }
342
343
        if ($this->options['is_sql']) {
344
            $this->queries[] = '-- ' . $this->driver->result('SELECT NOW()');
345
        }
346
347
        return [
348
            'headers' => $headers,
349
            'queries' => $this->queries,
350
        ];
351
    }
352
}
353