Passed
Push — main ( b31323...d7d864 )
by Thierry
02:22
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 in_array;
9
use function preg_match;
10
use function str_replace;
11
use function array_unique;
12
use function array_merge;
13
14
/**
15
 * Admin export functions
16
 */
17
class ExportAdmin extends AbstractAdmin
18
{
19
    use Traits\TableExportTrait;
20
    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...
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
     * Get data for export
38
     *
39
     * @param string $database      The database name
40
     * @param string $table
41
     *
42
     * @return array
43
     */
44
    public function getExportOptions(string $database, string $table = ''): array
45
    {
46
        $results = [
47
            'options' => $this->getBaseOptions($database, $table),
48
            'prefixes' => [],
49
            'labels' => [
50
                'export' => $this->trans->lang('Export'),
51
            ],
52
        ];
53
        if (($database)) {
54
            $results['tables'] = $this->getDbTables();
55
        } else {
56
            $results['databases'] = $this->getDatabases();
57
        }
58
        return $results;
59
    }
60
61
    /**
62
     * Dump routines and events in the connected database
63
     *
64
     * @param string $database      The database name
65
     *
66
     * @return void
67
     */
68
    private function dumpRoutinesAndEvents(string $database)
69
    {
70
        // From dump.inc.php
71
        $style = $this->options['db_style'];
72
        $queries = [];
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
                $queries[] = $this->driver->setUtf8mb4($create);
80
                if ($style != 'DROP+CREATE') {
81
                    $queries[] = 'DROP FUNCTION IF EXISTS ' . $this->driver->escapeId($row['Name']) . ';;';
82
                }
83
                $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
                $queries[] = $this->driver->setUtf8mb4($create);
90
                if ($style != 'DROP+CREATE') {
91
                    $queries[] = 'DROP PROCEDURE IF EXISTS ' . $this->driver->escapeId($row['Name']) . ';;';
92
                }
93
                $queries[] = "$create;;\n";
94
            }
95
        }
96
97
        if ($this->options['events']) {
98
            foreach ($this->driver->rows('SHOW EVENTS') as $row) {
99
                $sql = 'SHOW CREATE EVENT ' . $this->driver->escapeId($row['Name']);
100
                $create = $this->admin->removeDefiner($this->driver->result($sql, 3));
101
                $queries[] = $this->driver->setUtf8mb4($create);
102
                if ($style != 'DROP+CREATE') {
103
                    $queries[] = 'DROP EVENT IF EXISTS ' . $this->driver->escapeId($row['Name']) . ';;';
104
                }
105
                $queries[] = "$create;;\n";
106
            }
107
        }
108
109
        if (count($queries) > 0) {
110
            $this->queries[] = "DELIMITER ;;\n";
111
            foreach ($queries as $query) {
112
                $this->queries[] = $query;
113
            }
114
            $this->queries[] = "DELIMITER ;;\n";
115
        }
116
    }
117
118
    /**
119
     * @param string $table
120
     * @param bool $dumpTable
121
     * @param bool $dumpData
122
     *
123
     * @return void
124
     */
125
    private function dumpTable(string $table, bool $dumpTable, bool $dumpData)
126
    {
127
        $this->dumpTableOrView($table, ($dumpTable ? $this->options['table_style'] : ''));
128
        if ($dumpData) {
129
            $fields = $this->driver->fields($table);
130
            $query = 'SELECT *' . $this->driver->convertFields($fields, $fields) .
131
                ' FROM ' . $this->driver->table($table);
132
            $this->dumpData($table, $query);
133
        }
134
        if ($this->options['is_sql'] && $this->options['triggers'] && $dumpTable &&
135
            ($triggers = $this->driver->sqlForCreateTrigger($table))) {
136
            $this->queries[] = 'DELIMITER ;';
137
            $this->queries[] = $triggers;
138
            $this->queries[] = 'DELIMITER ;';
139
        }
140
        if ($this->options['is_sql']) {
141
            $this->queries[] = '';
142
        }
143
    }
144
145
    /**
146
     * Dump tables
147
     *
148
     * @param string $database      The database name
149
     *
150
     * @return void
151
     */
152
    private function dumpTables(string $database)
153
    {
154
        $dbDumpTable = $this->tables['list'] === '*' && in_array($database, $this->databases['list']);
155
        $dbDumpData = in_array($database, $this->databases['data']);
156
        $this->views = []; // View names
157
        $this->fkeys = []; // Table names for foreign keys
158
        $dbTables = $this->driver->tableStatuses(true);
159
        foreach ($dbTables as $table => $tableStatus) {
160
            $isView = $this->driver->isView($tableStatus);
161
            if ($isView) {
162
                // The views will be dumped after the tables
163
                $this->views[] = $table;
164
                continue;
165
            }
166
            $this->fkeys[] = $table;
167
            $dumpTable = $dbDumpTable || in_array($table, $this->tables['list']);
168
            $dumpData = $dbDumpData || in_array($table, $this->tables['data']);
169
            if ($dumpTable || $dumpData) {
170
                $this->dumpTable($table, $dumpTable, $dumpData);
171
            }
172
        }
173
    }
174
175
    /**
176
     * @return void
177
     */
178
    private function dumpViewsAndFKeys()
179
    {
180
        // Add FKs after creating tables (except in MySQL which uses SET FOREIGN_KEY_CHECKS=0)
181
        if ($this->driver->support('fkeys_sql')) {
182
            foreach ($this->fkeys as $table) {
183
                $this->queries[] = $this->driver->sqlForForeignKeys($table);
184
            }
185
        }
186
        // Dump the views after all the tables
187
        foreach ($this->views as $view) {
188
            $this->dumpTableOrView($view, $this->options['table_style'], 1);
189
        }
190
    }
191
192
    /**
193
     * @param string $database
194
     *
195
     * @return void
196
     */
197
    private function dumpDatabaseCreation(string $database)
198
    {
199
        $style = $this->options['db_style'];
200
        $this->driver->connect($database, '');
201
        $sql = 'SHOW CREATE DATABASE ' . $this->driver->escapeId($database);
202
        if ($this->options['is_sql'] && preg_match('~CREATE~', $style) &&
203
            ($create = $this->driver->result($sql, 1))) {
204
            $this->driver->setUtf8mb4($create);
205
            if ($style == 'DROP+CREATE') {
206
                $this->queries[] = 'DROP DATABASE IF EXISTS ' . $this->driver->escapeId($database) . ';';
207
            }
208
            $this->queries[] = $create . ";\n";
209
        }
210
    }
211
212
    /**
213
     * @param string $database
214
     *
215
     * @return void
216
     */
217
    private function dumpDatabase(string $database)
218
    {
219
        $this->dumpDatabaseCreation($database);
220
        if ($this->options['is_sql'] && $this->driver->jush() === 'sql') {
221
            // Dump routines and events currently works only for MySQL.
222
            if ($this->options['db_style']) {
223
                if (($query = $this->driver->sqlForUseDatabase($database))) {
224
                    $this->queries[] = $query . ';';
225
                }
226
                $this->queries[] = ''; // Empty line
227
            }
228
            $this->dumpRoutinesAndEvents($database);
229
        }
230
231
        if (!$this->options['table_style'] && !$this->options['data_style']) {
232
            return;
233
        }
234
235
        $this->dumpTables($database);
236
        $this->dumpViewsAndFKeys();
237
    }
238
239
    /**
240
     * Export databases
241
     *
242
     * @param array  $databases     The databases to dump
243
     * @param array  $tables        The tables to dump
244
     * @param array  $options       The export options
245
     *
246
     * @return array|string
247
     */
248
    public function exportDatabases(array $databases, array $tables, array $options)
249
    {
250
        // From dump.inc.php
251
        // $tables = array_flip($options['tables']) + array_flip($options['data']);
252
        // $ext = dump_headers((count($tables) == 1 ? key($tables) : DB), (DB == '' || count($tables) > 1));
253
        $options['is_sql'] = preg_match('~sql~', $options['format']);
254
        $this->databases = $databases;
255
        $this->tables = $tables;
256
        $this->options = $options;
257
258
        $headers = null;
259
        if ($this->options['is_sql']) {
260
            $headers = [
261
                'version' => $this->driver->version(),
262
                'driver' => $this->driver->name(),
263
                'server' => str_replace("\n", ' ', $this->driver->serverInfo()),
264
                'sql' => false,
265
                'data_style' => false,
266
            ];
267
            if ($this->driver->jush() == 'sql') {
268
                $headers['sql'] = true;
269
                if (isset($options['data_style'])) {
270
                    $headers['data_style'] = true;
271
                }
272
                // Set some options in database server
273
                $this->driver->query("SET time_zone = '+00:00'");
274
                $this->driver->query("SET sql_mode = ''");
275
            }
276
        }
277
278
        foreach (array_unique(array_merge($databases['list'], $databases['data'])) as $database) {
279
            try {
280
                $this->dumpDatabase($database);
281
            }
282
            catch (Exception $e) {
283
                return $e->getMessage();
284
            }
285
        }
286
287
        if ($this->options['is_sql']) {
288
            $this->queries[] = '-- ' . $this->driver->result('SELECT NOW()');
289
        }
290
291
        return [
292
            'headers' => $headers,
293
            'queries' => $this->queries,
294
        ];
295
    }
296
}
297