Passed
Push — master ( a7084c...bccfdb )
by William
08:29
created

libraries/classes/Plugins/Export/ExportXml.php (1 issue)

1
<?php
2
/* vim: set expandtab sw=4 ts=4 sts=4: */
3
/**
4
 * Set of functions used to build XML dumps of tables
5
 *
6
 * @package    PhpMyAdmin-Export
7
 * @subpackage XML
8
 */
9
declare(strict_types=1);
10
11
namespace PhpMyAdmin\Plugins\Export;
12
13
use PhpMyAdmin\DatabaseInterface;
14
use PhpMyAdmin\Export;
15
use PhpMyAdmin\Plugins\ExportPlugin;
16
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
17
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
18
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
19
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
20
use PhpMyAdmin\Properties\Options\Items\HiddenPropertyItem;
21
use PhpMyAdmin\Util;
22
23
/* Can't do server export */
24
if (! isset($GLOBALS['db']) || strlen($GLOBALS['db']) === 0) {
25
    $GLOBALS['skip_import'] = true;
26
    return;
27
}
28
29
/**
30
 * Handles the export for the XML class
31
 *
32
 * @package    PhpMyAdmin-Export
33
 * @subpackage XML
34
 */
35
class ExportXml extends ExportPlugin
36
{
37
    /**
38
     * Table name
39
     *
40
     * @var string
41
     */
42
    private $_table;
43
    /**
44
     * Table names
45
     *
46
     * @var array
47
     */
48
    private $_tables;
49
50
    /**
51
     * Constructor
52
     */
53
    public function __construct()
54
    {
55
        parent::__construct();
56
        $this->setProperties();
57
    }
58
59
    /**
60
     * Initialize the local variables that are used for export XML
61
     *
62
     * @return void
63
     */
64
    protected function initSpecificVariables()
65
    {
66
        global $table, $tables;
67
        $this->_setTable($table);
68
        if (is_array($tables)) {
69
            $this->_setTables($tables);
70
        }
71
    }
72
73
    /**
74
     * Sets the export XML properties
75
     *
76
     * @return void
77
     */
78
    protected function setProperties()
79
    {
80
        // create the export plugin property item
81
        $exportPluginProperties = new ExportPluginProperties();
82
        $exportPluginProperties->setText('XML');
83
        $exportPluginProperties->setExtension('xml');
84
        $exportPluginProperties->setMimeType('text/xml');
85
        $exportPluginProperties->setOptionsText(__('Options'));
86
87
        // create the root group that will be the options field for
88
        // $exportPluginProperties
89
        // this will be shown as "Format specific options"
90
        $exportSpecificOptions = new OptionsPropertyRootGroup(
91
            "Format Specific Options"
92
        );
93
94
        // general options main group
95
        $generalOptions = new OptionsPropertyMainGroup("general_opts");
96
        // create primary items and add them to the group
97
        $leaf = new HiddenPropertyItem("structure_or_data");
98
        $generalOptions->addProperty($leaf);
99
        // add the main group to the root group
100
        $exportSpecificOptions->addProperty($generalOptions);
101
102
        // export structure main group
103
        $structure = new OptionsPropertyMainGroup(
104
            "structure",
105
            __('Object creation options (all are recommended)')
106
        );
107
108
        // create primary items and add them to the group
109
        $leaf = new BoolPropertyItem(
110
            "export_events",
111
            __('Events')
112
        );
113
        $structure->addProperty($leaf);
114
        $leaf = new BoolPropertyItem(
115
            "export_functions",
116
            __('Functions')
117
        );
118
        $structure->addProperty($leaf);
119
        $leaf = new BoolPropertyItem(
120
            "export_procedures",
121
            __('Procedures')
122
        );
123
        $structure->addProperty($leaf);
124
        $leaf = new BoolPropertyItem(
125
            "export_tables",
126
            __('Tables')
127
        );
128
        $structure->addProperty($leaf);
129
        $leaf = new BoolPropertyItem(
130
            "export_triggers",
131
            __('Triggers')
132
        );
133
        $structure->addProperty($leaf);
134
        $leaf = new BoolPropertyItem(
135
            "export_views",
136
            __('Views')
137
        );
138
        $structure->addProperty($leaf);
139
        $exportSpecificOptions->addProperty($structure);
140
141
        // data main group
142
        $data = new OptionsPropertyMainGroup(
143
            "data",
144
            __('Data dump options')
145
        );
146
        // create primary items and add them to the group
147
        $leaf = new BoolPropertyItem(
148
            "export_contents",
149
            __('Export contents')
150
        );
151
        $data->addProperty($leaf);
152
        $exportSpecificOptions->addProperty($data);
153
154
        // set the options for the export plugin property item
155
        $exportPluginProperties->setOptions($exportSpecificOptions);
156
        $this->properties = $exportPluginProperties;
157
    }
158
159
    /**
160
     * Generates output for SQL defintions of routines
161
     *
162
     * @param string $db      Database name
163
     * @param string $type    Item type to be used in XML output
164
     * @param string $dbitype Item type used in DBI qieries
165
     *
166
     * @return string XML with definitions
167
     */
168
    private function _exportRoutines($db, $type, $dbitype)
169
    {
170
        // Export routines
171
        $routines = $GLOBALS['dbi']->getProceduresOrFunctions(
172
            $db,
173
            $dbitype
174
        );
175
        return $this->_exportDefinitions($db, $type, $dbitype, $routines);
176
    }
177
178
    /**
179
     * Generates output for SQL defintions
180
     *
181
     * @param string $db      Database name
182
     * @param string $type    Item type to be used in XML output
183
     * @param string $dbitype Item type used in DBI qieries
184
     * @param array  $names   Names of items to export
185
     *
186
     * @return string XML with definitions
187
     */
188
    private function _exportDefinitions($db, $type, $dbitype, array $names)
189
    {
190
        global $crlf;
191
192
        $head = '';
193
194
        if ($names) {
195
            foreach ($names as $name) {
196
                $head .= '            <pma:' . $type . ' name="'
197
                    . htmlspecialchars($name) . '">' . $crlf;
198
199
                // Do some formatting
200
                $sql = $GLOBALS['dbi']->getDefinition($db, $dbitype, $name);
201
                $sql = htmlspecialchars(rtrim($sql));
202
                $sql = str_replace("\n", "\n                ", $sql);
203
204
                $head .= "                " . $sql . $crlf;
205
                $head .= '            </pma:' . $type . '>' . $crlf;
206
            }
207
        }
208
209
        return $head;
210
    }
211
212
    /**
213
     * Outputs export header. It is the first method to be called, so all
214
     * the required variables are initialized here.
215
     *
216
     * @return bool Whether it succeeded
217
     */
218
    public function exportHeader()
219
    {
220
        $this->initSpecificVariables();
221
        global $crlf, $cfg, $db;
222
        $table = $this->_getTable();
223
        $tables = $this->_getTables();
224
225
        $export_struct = isset($GLOBALS['xml_export_functions'])
226
            || isset($GLOBALS['xml_export_procedures'])
227
            || isset($GLOBALS['xml_export_tables'])
228
            || isset($GLOBALS['xml_export_triggers'])
229
            || isset($GLOBALS['xml_export_views']);
230
        $export_data = isset($GLOBALS['xml_export_contents']) ? true : false;
231
232
        if ($GLOBALS['output_charset_conversion']) {
233
            $charset = $GLOBALS['charset'];
234
        } else {
235
            $charset = 'utf-8';
236
        }
237
238
        $head = '<?xml version="1.0" encoding="' . $charset . '"?>' . $crlf
239
            . '<!--' . $crlf
240
            . '- phpMyAdmin XML Dump' . $crlf
241
            . '- version ' . PMA_VERSION . $crlf
242
            . '- https://www.phpmyadmin.net' . $crlf
243
            . '-' . $crlf
244
            . '- ' . __('Host:') . ' ' . htmlspecialchars($cfg['Server']['host']);
245
        if (! empty($cfg['Server']['port'])) {
246
            $head .= ':' . $cfg['Server']['port'];
247
        }
248
        $head .= $crlf
249
            . '- ' . __('Generation Time:') . ' '
250
            . Util::localisedDate() . $crlf
251
            . '- ' . __('Server version:') . ' ' . $GLOBALS['dbi']->getVersionString() . $crlf
252
            . '- ' . __('PHP Version:') . ' ' . phpversion() . $crlf
253
            . '-->' . $crlf . $crlf;
254
255
        $head .= '<pma_xml_export version="1.0"'
256
            . ($export_struct
257
                ? ' xmlns:pma="https://www.phpmyadmin.net/some_doc_url/"'
258
                : '')
259
            . '>' . $crlf;
260
261
        if ($export_struct) {
262
            $result = $GLOBALS['dbi']->fetchResult(
263
                'SELECT `DEFAULT_CHARACTER_SET_NAME`, `DEFAULT_COLLATION_NAME`'
264
                . ' FROM `information_schema`.`SCHEMATA` WHERE `SCHEMA_NAME`'
265
                . ' = \'' . $GLOBALS['dbi']->escapeString($db) . '\' LIMIT 1'
266
            );
267
            $db_collation = $result[0]['DEFAULT_COLLATION_NAME'];
268
            $db_charset = $result[0]['DEFAULT_CHARACTER_SET_NAME'];
269
270
            $head .= '    <!--' . $crlf;
271
            $head .= '    - Structure schemas' . $crlf;
272
            $head .= '    -->' . $crlf;
273
            $head .= '    <pma:structure_schemas>' . $crlf;
274
            $head .= '        <pma:database name="' . htmlspecialchars($db)
275
                . '" collation="' . htmlspecialchars($db_collation) . '" charset="' . htmlspecialchars($db_charset)
276
                . '">' . $crlf;
277
278
            if (is_null($tables)) {
279
                $tables = array();
0 ignored issues
show
Short array syntax must be used to define arrays
Loading history...
280
            }
281
282
            if (count($tables) === 0) {
283
                $tables[] = $table;
284
            }
285
286
            foreach ($tables as $table) {
287
                // Export tables and views
288
                $result = $GLOBALS['dbi']->fetchResult(
289
                    'SHOW CREATE TABLE ' . Util::backquote($db) . '.'
290
                    . Util::backquote($table),
291
                    0
292
                );
293
                $tbl = $result[$table][1];
294
295
                $is_view = $GLOBALS['dbi']->getTable($db, $table)
296
                    ->isView();
297
298
                if ($is_view) {
299
                    $type = 'view';
300
                } else {
301
                    $type = 'table';
302
                }
303
304
                if ($is_view && ! isset($GLOBALS['xml_export_views'])) {
305
                    continue;
306
                }
307
308
                if (! $is_view && ! isset($GLOBALS['xml_export_tables'])) {
309
                    continue;
310
                }
311
312
                $head .= '            <pma:' . $type . ' name="' . htmlspecialchars($table) . '">'
313
                    . $crlf;
314
315
                $tbl = "                " . htmlspecialchars($tbl);
316
                $tbl = str_replace("\n", "\n                ", $tbl);
317
318
                $head .= $tbl . ';' . $crlf;
319
                $head .= '            </pma:' . $type . '>' . $crlf;
320
321
                if (isset($GLOBALS['xml_export_triggers'])
322
                    && $GLOBALS['xml_export_triggers']
323
                ) {
324
                    // Export triggers
325
                    $triggers = $GLOBALS['dbi']->getTriggers($db, $table);
326
                    if ($triggers) {
327
                        foreach ($triggers as $trigger) {
328
                            $code = $trigger['create'];
329
                            $head .= '            <pma:trigger name="'
330
                                . htmlspecialchars($trigger['name']) . '">' . $crlf;
331
332
                            // Do some formatting
333
                            $code = mb_substr(rtrim($code), 0, -3);
334
                            $code = "                " . htmlspecialchars($code);
335
                            $code = str_replace("\n", "\n                ", $code);
336
337
                            $head .= $code . $crlf;
338
                            $head .= '            </pma:trigger>' . $crlf;
339
                        }
340
341
                        unset($trigger);
342
                        unset($triggers);
343
                    }
344
                }
345
            }
346
347
            if (isset($GLOBALS['xml_export_functions'])
348
                && $GLOBALS['xml_export_functions']
349
            ) {
350
                $head .= $this->_exportRoutines($db, 'function', 'FUNCTION');
351
            }
352
353
            if (isset($GLOBALS['xml_export_procedures'])
354
                && $GLOBALS['xml_export_procedures']
355
            ) {
356
                $head .= $this->_exportRoutines($db, 'procedure', 'PROCEDURE');
357
            }
358
359
            if (isset($GLOBALS['xml_export_events'])
360
                && $GLOBALS['xml_export_events']
361
            ) {
362
                // Export events
363
                $events = $GLOBALS['dbi']->fetchResult(
364
                    "SELECT EVENT_NAME FROM information_schema.EVENTS "
365
                    . "WHERE EVENT_SCHEMA='" . $GLOBALS['dbi']->escapeString($db)
366
                    . "'"
367
                );
368
                $head .= $this->_exportDefinitions(
369
                    $db,
370
                    'event',
371
                    'EVENT',
372
                    $events
373
                );
374
            }
375
376
            unset($result);
377
378
            $head .= '        </pma:database>' . $crlf;
379
            $head .= '    </pma:structure_schemas>' . $crlf;
380
381
            if ($export_data) {
382
                $head .= $crlf;
383
            }
384
        }
385
386
        return $this->export->outputHandler($head);
387
    }
388
389
    /**
390
     * Outputs export footer
391
     *
392
     * @return bool Whether it succeeded
393
     */
394
    public function exportFooter()
395
    {
396
        $foot = '</pma_xml_export>';
397
398
        return $this->export->outputHandler($foot);
399
    }
400
401
    /**
402
     * Outputs database header
403
     *
404
     * @param string $db       Database name
405
     * @param string $db_alias Aliases of db
406
     *
407
     * @return bool Whether it succeeded
408
     */
409
    public function exportDBHeader($db, $db_alias = '')
410
    {
411
        global $crlf;
412
413
        if (empty($db_alias)) {
414
            $db_alias = $db;
415
        }
416
        if (isset($GLOBALS['xml_export_contents'])
417
            && $GLOBALS['xml_export_contents']
418
        ) {
419
            $head = '    <!--' . $crlf
420
                . '    - ' . __('Database:') . ' ' . '\''
421
                . htmlspecialchars($db_alias) . '\'' . $crlf
422
                . '    -->' . $crlf . '    <database name="'
423
                . htmlspecialchars($db_alias) . '">' . $crlf;
424
425
            return $this->export->outputHandler($head);
426
        }
427
428
        return true;
429
    }
430
431
    /**
432
     * Outputs database footer
433
     *
434
     * @param string $db Database name
435
     *
436
     * @return bool Whether it succeeded
437
     */
438
    public function exportDBFooter($db)
439
    {
440
        global $crlf;
441
442
        if (isset($GLOBALS['xml_export_contents'])
443
            && $GLOBALS['xml_export_contents']
444
        ) {
445
            return $this->export->outputHandler('    </database>' . $crlf);
446
        }
447
448
        return true;
449
    }
450
451
    /**
452
     * Outputs CREATE DATABASE statement
453
     *
454
     * @param string $db          Database name
455
     * @param string $export_type 'server', 'database', 'table'
456
     * @param string $db_alias    Aliases of db
457
     *
458
     * @return bool Whether it succeeded
459
     */
460
    public function exportDBCreate($db, $export_type, $db_alias = '')
461
    {
462
        return true;
463
    }
464
465
    /**
466
     * Outputs the content of a table in XML format
467
     *
468
     * @param string $db        database name
469
     * @param string $table     table name
470
     * @param string $crlf      the end of line sequence
471
     * @param string $error_url the url to go back in case of error
472
     * @param string $sql_query SQL query for obtaining data
473
     * @param array  $aliases   Aliases of db/table/columns
474
     *
475
     * @return bool Whether it succeeded
476
     */
477
    public function exportData(
478
        $db,
479
        $table,
480
        $crlf,
481
        $error_url,
482
        $sql_query,
483
        array $aliases = []
484
    ) {
485
        // Do not export data for merge tables
486
        if ($GLOBALS['dbi']->getTable($db, $table)->isMerge()) {
487
            return true;
488
        }
489
490
        $db_alias = $db;
491
        $table_alias = $table;
492
        $this->initAlias($aliases, $db_alias, $table_alias);
493
        if (isset($GLOBALS['xml_export_contents'])
494
            && $GLOBALS['xml_export_contents']
495
        ) {
496
            $result = $GLOBALS['dbi']->query(
497
                $sql_query,
498
                DatabaseInterface::CONNECT_USER,
499
                DatabaseInterface::QUERY_UNBUFFERED
500
            );
501
502
            $columns_cnt = $GLOBALS['dbi']->numFields($result);
503
            $columns = [];
504
            for ($i = 0; $i < $columns_cnt; $i++) {
505
                $columns[$i] = stripslashes($GLOBALS['dbi']->fieldName($result, $i));
506
            }
507
            unset($i);
508
509
            $buffer = '        <!-- ' . __('Table') . ' '
510
                . htmlspecialchars($table_alias) . ' -->' . $crlf;
511
            if (! $this->export->outputHandler($buffer)) {
512
                return false;
513
            }
514
515
            while ($record = $GLOBALS['dbi']->fetchRow($result)) {
516
                $buffer = '        <table name="'
517
                    . htmlspecialchars($table_alias) . '">' . $crlf;
518
                for ($i = 0; $i < $columns_cnt; $i++) {
519
                    $col_as = $columns[$i];
520
                    if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])
521
                    ) {
522
                        $col_as
523
                            = $aliases[$db]['tables'][$table]['columns'][$col_as];
524
                    }
525
                    // If a cell is NULL, still export it to preserve
526
                    // the XML structure
527
                    if (! isset($record[$i]) || is_null($record[$i])) {
528
                        $record[$i] = 'NULL';
529
                    }
530
                    $buffer .= '            <column name="'
531
                        . htmlspecialchars($col_as) . '">'
532
                        . htmlspecialchars((string) $record[$i])
533
                        . '</column>' . $crlf;
534
                }
535
                $buffer .= '        </table>' . $crlf;
536
537
                if (! $this->export->outputHandler($buffer)) {
538
                    return false;
539
                }
540
            }
541
            $GLOBALS['dbi']->freeResult($result);
542
        }
543
544
        return true;
545
    }
546
547
548
    /* ~~~~~~~~~~~~~~~~~~~~ Getters and Setters ~~~~~~~~~~~~~~~~~~~~ */
549
550
    /**
551
     * Gets the table name
552
     *
553
     * @return string
554
     */
555
    private function _getTable()
556
    {
557
        return $this->_table;
558
    }
559
560
    /**
561
     * Sets the table name
562
     *
563
     * @param string $table table name
564
     *
565
     * @return void
566
     */
567
    private function _setTable($table)
568
    {
569
        $this->_table = $table;
570
    }
571
572
    /**
573
     * Gets the table names
574
     *
575
     * @return array
576
     */
577
    private function _getTables()
578
    {
579
        return $this->_tables;
580
    }
581
582
    /**
583
     * Sets the table names
584
     *
585
     * @param array $tables table names
586
     *
587
     * @return void
588
     */
589
    private function _setTables(array $tables)
590
    {
591
        $this->_tables = $tables;
592
    }
593
}
594