Completed
Push — master ( 411fd8...9ec5e1 )
by diego
04:00 queued 02:26
created

Mysqldump::exportEvents()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
nop 0
1
<?php
2
/**
3
 * Mysqldump File Doc Comment
4
 *
5
 * PHP version 5
6
 *
7
 * @category Library
8
 * @package  Ifsnop\Mysqldump
9
 * @author   Michael J. Calkins <[email protected]>
10
 * @author   Diego Torres <[email protected]>
11
 * @license  http://www.gnu.org/copyleft/gpl.html GNU General Public License
12
 * @link     https://github.com/ifsnop/mysqldump-php
13
 *
14
 */
15
16
namespace Ifsnop\Mysqldump;
17
18
use Exception;
19
use PDO;
20
use PDOException;
21
22
/**
23
 * Mysqldump Class Doc Comment
24
 *
25
 * @category Library
26
 * @package  Ifsnop\Mysqldump
27
 * @author   Michael J. Calkins <[email protected]>
28
 * @author   Diego Torres <[email protected]>
29
 * @license  http://www.gnu.org/copyleft/gpl.html GNU General Public License
30
 * @link     https://github.com/ifsnop/mysqldump-php
31
 *
32
 */
33
class Mysqldump
34
{
35
36
    // Same as mysqldump
37
    const MAXLINESIZE = 1000000;
38
39
    // Available compression methods as constants
40
    const GZIP = 'Gzip';
41
    const BZIP2 = 'Bzip2';
42
    const NONE = 'None';
43
44
    // Available connection strings
45
    const UTF8 = 'utf8';
46
    const UTF8MB4 = 'utf8mb4';
47
48
    /**
49
    * Database username
50
    * @var string
51
    */
52
    public $user;
53
    /**
54
    * Database password
55
    * @var string
56
    */
57
    public $pass;
58
    /**
59
    * Connection string for PDO
60
    * @var string
61
    */
62
    public $dsn;
63
    /**
64
    * Destination filename, defaults to stdout
65
    * @var string
66
    */
67
    public $fileName = 'php://output';
68
69
    // Internal stuff
70
    private $tables = array();
71
    private $views = array();
72
    private $triggers = array();
73
    private $procedures = array();
74
    private $events = array();
75
    private $dbHandler = null;
76
    private $dbType;
77
    private $compressManager;
78
    private $typeAdapter;
79
    private $dumpSettings = array();
80
    private $pdoSettings = array();
81
    private $version;
82
    private $tableColumnTypes = array();
83
    /**
84
    * database name, parsed from dsn
85
    * @var string
86
    */
87
    private $dbName;
88
    /**
89
    * host name, parsed from dsn
90
    * @var string
91
    */
92
    private $host;
93
    /**
94
    * dsn string parsed as an array
95
    * @var array
96
    */
97
    private $dsnArray = array();
98
99
    /**
100
     * Constructor of Mysqldump. Note that in the case of an SQLite database
101
     * connection, the filename must be in the $db parameter.
102
     *
103
     * @param string $dsn        PDO DSN connection string
104
     * @param string $user       SQL account username
105
     * @param string $pass       SQL account password
106
     * @param array  $dumpSettings SQL database settings
107
     * @param array  $pdoSettings  PDO configured attributes
108
     */
109
    public function __construct(
110
        $dsn = '',
111
        $user = '',
112
        $pass = '',
113
        $dumpSettings = array(),
114
        $pdoSettings = array()
115
    ) {
116
        $dumpSettingsDefault = array(
117
            'include-tables' => array(),
118
            'exclude-tables' => array(),
119
            'compress' => Mysqldump::NONE,
120
            'init_commands' => array(),
121
            'no-data' => array(),
122
            'reset-auto-increment' => false,
123
            'add-drop-database' => false,
124
            'add-drop-table' => false,
125
            'add-drop-trigger' => true,
126
            'add-locks' => true,
127
            'complete-insert' => false,
128
            'databases' => false,
129
            'default-character-set' => Mysqldump::UTF8,
130
            'disable-keys' => true,
131
            'extended-insert' => true,
132
            'events' => false,
133
            'hex-blob' => true, /* faster than escaped content */
134
            'net_buffer_length' => self::MAXLINESIZE,
135
            'no-autocommit' => true,
136
            'no-create-info' => false,
137
            'lock-tables' => true,
138
            'routines' => false,
139
            'single-transaction' => true,
140
            'skip-triggers' => false,
141
            'skip-tz-utc' => false,
142
            'skip-comments' => false,
143
            'skip-dump-date' => false,
144
            'where' => '',
145
            /* deprecated */
146
            'disable-foreign-keys-check' => true
147
        );
148
149
        $pdoSettingsDefault = array(
150
            PDO::ATTR_PERSISTENT => true,
151
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
152
            PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false
153
        );
154
155
        $this->user = $user;
156
        $this->pass = $pass;
157
        $this->parseDsn($dsn);
158
        $this->pdoSettings = self::array_replace_recursive($pdoSettingsDefault, $pdoSettings);
159
        $this->dumpSettings = self::array_replace_recursive($dumpSettingsDefault, $dumpSettings);
160
161
        $this->dumpSettings['init_commands'][] = "SET NAMES " . $this->dumpSettings['default-character-set'];
162
163
        if (false === $this->dumpSettings['skip-tz-utc']) {
164
            $this->dumpSettings['init_commands'][] = "SET TIME_ZONE='+00:00'";
165
        }
166
167
        $diff = array_diff(array_keys($this->dumpSettings), array_keys($dumpSettingsDefault));
168
        if (count($diff)>0) {
169
            throw new Exception("Unexpected value in dumpSettings: (" . implode(",", $diff) . ")");
170
        }
171
172
        if ( !is_array($this->dumpSettings['include-tables']) ||
173
            !is_array($this->dumpSettings['exclude-tables']) ) {
174
            throw new Exception("Include-tables and exclude-tables should be arrays");
175
        }
176
177
        // Dump the same views as tables, mimic mysqldump behaviour
178
        $this->dumpSettings['include-views'] = $this->dumpSettings['include-tables'];
179
180
        // Create a new compressManager to manage compressed output
181
        $this->compressManager = CompressManagerFactory::create($this->dumpSettings['compress']);
182
    }
183
184
    /**
185
     * Destructor of Mysqldump. Unsets dbHandlers and database objects.
186
     *
187
     */
188
    public function __destruct()
189
    {
190
        $this->dbHandler = null;
191
    }
192
193
    /**
194
     * Custom array_replace_recursive to be used if PHP < 5.3
195
     * Replaces elements from passed arrays into the first array recursively
196
     *
197
     * @param array $array1 The array in which elements are replaced
198
     * @param array $array2 The array from which elements will be extracted
199
     *
200
     * @return array Returns an array, or NULL if an error occurs.
201
     */
202
    public static function array_replace_recursive($array1, $array2)
203
    {
204
        if (function_exists('array_replace_recursive')) {
205
            return array_replace_recursive($array1, $array2);
206
        }
207
208
        foreach ($array2 as $key => $value) {
209
            if (is_array($value)) {
210
                $array1[$key] = self::array_replace_recursive($array1[$key], $value);
211
            } else {
212
                $array1[$key] = $value;
213
            }
214
        }
215
        return $array1;
216
    }
217
218
    /**
219
     * Parse DSN string and extract dbname value
220
     * Several examples of a DSN string
221
     *   mysql:host=localhost;dbname=testdb
222
     *   mysql:host=localhost;port=3307;dbname=testdb
223
     *   mysql:unix_socket=/tmp/mysql.sock;dbname=testdb
224
     *
225
     * @param string $dsn dsn string to parse
226
     */
227
    private function parseDsn($dsn)
228
    {
229
        if (empty($dsn) || (false === ($pos = strpos($dsn, ":")))) {
230
            throw new Exception("Empty DSN string");
231
        }
232
233
        $this->dsn = $dsn;
234
        $this->dbType = strtolower(substr($dsn, 0, $pos));
235
236
        if (empty($this->dbType)) {
237
            throw new Exception("Missing database type from DSN string");
238
        }
239
240
        $dsn = substr($dsn, $pos + 1);
241
242
        foreach(explode(";", $dsn) as $kvp) {
243
            $kvpArr = explode("=", $kvp);
244
            $this->dsnArray[strtolower($kvpArr[0])] = $kvpArr[1];
245
        }
246
247
        if (empty($this->dsnArray['host']) &&
248
            empty($this->dsnArray['unix_socket'])) {
249
            throw new Exception("Missing host from DSN string");
250
        }
251
        $this->host = (!empty($this->dsnArray['host'])) ?
252
            $this->dsnArray['host'] :
253
            $this->dsnArray['unix_socket'];
254
255
        if (empty($this->dsnArray['dbname'])) {
256
            throw new Exception("Missing database name from DSN string");
257
        }
258
259
        $this->dbName = $this->dsnArray['dbname'];
260
261
        return true;
262
    }
263
264
    /**
265
     * Connect with PDO
266
     *
267
     * @return null
268
     */
269
    private function connect()
270
    {
271
        // Connecting with PDO
272
        try {
273
            switch ($this->dbType) {
274
                case 'sqlite':
275
                    $this->dbHandler = @new PDO("sqlite:" . $this->dbName, null, null, $this->pdoSettings);
276
                    break;
277
                case 'mysql':
278
                case 'pgsql':
279
                case 'dblib':
280
                    $this->dbHandler = @new PDO(
281
                        $this->dsn,
282
                        $this->user,
283
                        $this->pass,
284
                        $this->pdoSettings
285
                    );
286
                    // Execute init commands once connected
287
                    foreach($this->dumpSettings['init_commands'] as $stmt) {
288
                        $this->dbHandler->exec($stmt);
289
                    }
290
                    // Store server version
291
                    $this->version = $this->dbHandler->getAttribute(PDO::ATTR_SERVER_VERSION);
292
                    break;
293
                default:
294
                    throw new Exception("Unsupported database type (" . $this->dbType . ")");
295
            }
296
        } catch (PDOException $e) {
297
            throw new Exception(
298
                "Connection to " . $this->dbType . " failed with message: " .
299
                $e->getMessage()
300
            );
301
        }
302
303
        if ( is_null($this->dbHandler) ) {
304
            throw new Exception("Connection to ". $this->dbType . "failed");
305
        }
306
307
        $this->dbHandler->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_NATURAL);
308
        $this->typeAdapter = TypeAdapterFactory::create($this->dbType, $this->dbHandler);
309
    }
310
311
    /**
312
     * Main call
313
     *
314
     * @param string $filename  Name of file to write sql dump to
315
     * @return null
316
     */
317
    public function start($filename = '')
318
    {
319
        // Output file can be redefined here
320
        if (!empty($filename)) {
321
            $this->fileName = $filename;
322
        }
323
324
        // Connect to database
325
        $this->connect();
326
327
        // Create output file
328
        $this->compressManager->open($this->fileName);
329
330
        // Write some basic info to output file
331
        $this->compressManager->write($this->getDumpFileHeader());
332
333
        // Store server settings and use sanner defaults to dump
334
        $this->compressManager->write(
335
            $this->typeAdapter->backup_parameters($this->dumpSettings)
336
        );
337
338
        if ($this->dumpSettings['databases']) {
339
            $this->compressManager->write(
340
                $this->typeAdapter->getDatabaseHeader($this->dbName)
341
            );
342
            if ($this->dumpSettings['add-drop-database']) {
343
                $this->compressManager->write(
344
                    $this->typeAdapter->add_drop_database($this->dbName)
345
                );
346
            }
347
        }
348
349
        // Get table, view and trigger structures from database
350
        $this->getDatabaseStructure();
351
352
        if ($this->dumpSettings['databases']) {
353
            $this->compressManager->write(
354
                $this->typeAdapter->databases($this->dbName)
355
            );
356
        }
357
358
        // If there still are some tables/views in include-tables array,
359
        // that means that some tables or views weren't found.
360
        // Give proper error and exit.
361
        // This check will be removed once include-tables supports regexps
362
        if (0 < count($this->dumpSettings['include-tables'])) {
363
            $name = implode(",", $this->dumpSettings['include-tables']);
364
            throw new Exception("Table (" . $name . ") not found in database");
365
        }
366
367
        $this->exportTables();
368
        $this->exportViews();
369
        $this->exportTriggers();
370
        $this->exportProcedures();
371
        $this->exportEvents();
372
373
        // Restore saved parameters
374
        $this->compressManager->write(
375
            $this->typeAdapter->restore_parameters($this->dumpSettings)
376
        );
377
        // Write some stats to output file
378
        $this->compressManager->write($this->getDumpFileFooter());
379
        // Close output file
380
        $this->compressManager->close();
381
    }
382
383
    /**
384
     * Returns header for dump file
385
     *
386
     * @return string
387
     */
388
    private function getDumpFileHeader()
389
    {
390
        $header = '';
391
        if ( !$this->dumpSettings['skip-comments'] ) {
392
            // Some info about software, source and time
393
            $header = "-- mysqldump-php https://github.com/ifsnop/mysqldump-php" . PHP_EOL .
394
                    "--" . PHP_EOL .
395
                    "-- Host: {$this->host}\tDatabase: {$this->dbName}" . PHP_EOL .
396
                    "-- ------------------------------------------------------" . PHP_EOL;
397
398
            if ( !empty($this->version) ) {
399
                $header .= "-- Server version \t" . $this->version . PHP_EOL;
400
            }
401
402
            if ( !$this->dumpSettings['skip-dump-date'] ) {
403
                $header .= "-- Date: " . date('r') . PHP_EOL . PHP_EOL;
404
            }
405
        }
406
        return $header;
407
    }
408
409
    /**
410
     * Returns footer for dump file
411
     *
412
     * @return string
413
     */
414
    private function getDumpFileFooter()
415
    {
416
        $footer = '';
417
        if (!$this->dumpSettings['skip-comments']) {
418
            $footer .= '-- Dump completed';
419
            if (!$this->dumpSettings['skip-dump-date']) {
420
                $footer .= ' on: ' . date('r');
421
            }
422
            $footer .= PHP_EOL;
423
        }
424
425
        return $footer;
426
    }
427
428
    /**
429
     * Reads table and views names from database.
430
     * Fills $this->tables array so they will be dumped later.
431
     *
432
     * @return null
433
     */
434
    private function getDatabaseStructure()
435
    {
436
        // Listing all tables from database
437 View Code Duplication
        if (empty($this->dumpSettings['include-tables'])) {
438
            // include all tables for now, blacklisting happens later
439
            foreach ($this->dbHandler->query($this->typeAdapter->show_tables($this->dbName)) as $row) {
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
440
                array_push($this->tables, current($row));
441
            }
442
        } else {
443
            // include only the tables mentioned in include-tables
444
            foreach ($this->dbHandler->query($this->typeAdapter->show_tables($this->dbName)) as $row) {
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
445
                if (in_array(current($row), $this->dumpSettings['include-tables'], true)) {
446
                    array_push($this->tables, current($row));
447
                    $elem = array_search(
448
                        current($row),
449
                        $this->dumpSettings['include-tables']
450
                    );
451
                    unset($this->dumpSettings['include-tables'][$elem]);
452
                }
453
            }
454
        }
455
456
        // Listing all views from database
457 View Code Duplication
        if (empty($this->dumpSettings['include-views'])) {
458
            // include all views for now, blacklisting happens later
459
            foreach ($this->dbHandler->query($this->typeAdapter->show_views($this->dbName)) as $row) {
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
460
                array_push($this->views, current($row));
461
            }
462
        } else {
463
            // include only the tables mentioned in include-tables
464
            foreach ($this->dbHandler->query($this->typeAdapter->show_views($this->dbName)) as $row) {
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
465
                if (in_array(current($row), $this->dumpSettings['include-views'], true)) {
466
                    array_push($this->views, current($row));
467
                    $elem = array_search(
468
                        current($row),
469
                        $this->dumpSettings['include-views']
470
                    );
471
                    unset($this->dumpSettings['include-views'][$elem]);
472
                }
473
            }
474
        }
475
476
        // Listing all triggers from database
477 View Code Duplication
        if (false === $this->dumpSettings['skip-triggers']) {
478
            foreach ($this->dbHandler->query($this->typeAdapter->show_triggers($this->dbName)) as $row) {
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
479
                array_push($this->triggers, $row['Trigger']);
480
            }
481
        }
482
483
        // Listing all procedures from database
484 View Code Duplication
        if ($this->dumpSettings['routines']) {
485
            foreach ($this->dbHandler->query($this->typeAdapter->show_procedures($this->dbName)) as $row) {
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
486
                array_push($this->procedures, $row['procedure_name']);
487
            }
488
        }
489
490
        // Listing all events from database
491 View Code Duplication
        if ($this->dumpSettings['events']) {
492
            foreach ($this->dbHandler->query($this->typeAdapter->show_events($this->dbName)) as $row) {
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
493
                array_push($this->events, $row['event_name']);
494
            }
495
        }
496
    }
497
498
    /**
499
     * Compare if $table name matches with a definition inside $arr
500
     * @param $table string
501
     * @param $arr array with strings or patterns
502
     * @return bool
503
     */
504
    private function matches($table, $arr) {
505
        $match = false;
506
507
        foreach ($arr as $pattern) {
508
            if ( '/' != $pattern[0] ) {
509
                continue;
510
            }
511
            if ( 1 == preg_match($pattern, $table) ) {
512
                $match = true;
513
            }
514
        }
515
516
        return in_array($table, $arr) || $match;
517
    }
518
519
    /**
520
     * Exports all the tables selected from database
521
     *
522
     * @return null
523
     */
524
    private function exportTables()
525
    {
526
        // Exporting tables one by one
527
        foreach ($this->tables as $table) {
528
            if ( $this->matches($table, $this->dumpSettings['exclude-tables']) ) {
529
                continue;
530
            }
531
            $this->getTableStructure($table);
532
            if ( false === $this->dumpSettings['no-data'] ) { // don't break compatibility with old trigger
533
                $this->listValues($table);
534
            } else if ( true === $this->dumpSettings['no-data']
535
                 || $this->matches($table, $this->dumpSettings['no-data']) ) {
536
                continue;
537
            } else {
538
                $this->listValues($table);
539
            }
540
        }
541
    }
542
543
    /**
544
     * Exports all the views found in database
545
     *
546
     * @return null
547
     */
548
    private function exportViews()
549
    {
550
        if (false === $this->dumpSettings['no-create-info']) {
551
            // Exporting views one by one
552
            foreach ($this->views as $view) {
553
                if ( $this->matches($view, $this->dumpSettings['exclude-tables']) ) {
554
                    continue;
555
                }
556
                $this->tableColumnTypes[$view] = $this->getTableColumnTypes($view);
557
                $this->getViewStructureTable($view);
558
            }
559
            foreach ($this->views as $view) {
560
                if ( $this->matches($view, $this->dumpSettings['exclude-tables']) ) {
561
                    continue;
562
                }
563
                $this->getViewStructureView($view);
564
            }
565
        }
566
    }
567
568
    /**
569
     * Exports all the triggers found in database
570
     *
571
     * @return null
572
     */
573
    private function exportTriggers()
574
    {
575
        // Exporting triggers one by one
576
        foreach ($this->triggers as $trigger) {
577
            $this->getTriggerStructure($trigger);
578
        }
579
    }
580
581
    /**
582
     * Exports all the procedures found in database
583
     *
584
     * @return null
585
     */
586
    private function exportProcedures()
587
    {
588
        // Exporting triggers one by one
589
        foreach ($this->procedures as $procedure) {
590
            $this->getProcedureStructure($procedure);
591
        }
592
    }
593
594
    /**
595
     * Exports all the events found in database
596
     *
597
     * @return null
598
     */
599
    private function exportEvents()
600
    {
601
        // Exporting triggers one by one
602
        foreach ($this->events as $event) {
603
            $this->getEventStructure($event);
604
        }
605
    }
606
607
    /**
608
     * Table structure extractor
609
     *
610
     * @todo move specific mysql code to typeAdapter
611
     * @param string $tableName  Name of table to export
612
     * @return null
613
     */
614
    private function getTableStructure($tableName)
615
    {
616
        if (!$this->dumpSettings['no-create-info']) {
617
            $ret = '';
618
            if (!$this->dumpSettings['skip-comments']) {
619
                $ret = "--" . PHP_EOL .
620
                    "-- Table structure for table `$tableName`" . PHP_EOL .
621
                    "--" . PHP_EOL . PHP_EOL;
622
            }
623
            $stmt = $this->typeAdapter->show_create_table($tableName);
624
            foreach ($this->dbHandler->query($stmt) as $r) {
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
625
                $this->compressManager->write($ret);
626
                if ($this->dumpSettings['add-drop-table']) {
627
                    $this->compressManager->write(
628
                        $this->typeAdapter->drop_table($tableName)
629
                    );
630
                }
631
                $this->compressManager->write(
632
                    $this->typeAdapter->create_table($r, $this->dumpSettings)
633
                );
634
                break;
635
            }
636
        }
637
        $this->tableColumnTypes[$tableName] = $this->getTableColumnTypes($tableName);
638
        return;
639
    }
640
641
    /**
642
     * Store column types to create data dumps and for Stand-In tables
643
     *
644
     * @param string $tableName  Name of table to export
645
     * @return array type column types detailed
646
     */
647
648
    private function getTableColumnTypes($tableName) {
649
        $columnTypes = array();
650
        $columns = $this->dbHandler->query(
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
651
            $this->typeAdapter->show_columns($tableName)
652
        );
653
        $columns->setFetchMode(PDO::FETCH_ASSOC);
654
655
        foreach($columns as $key => $col) {
656
            $types = $this->typeAdapter->parseColumnType($col);
657
            $columnTypes[$col['Field']] = array(
658
                'is_numeric'=> $types['is_numeric'],
659
                'is_blob' => $types['is_blob'],
660
                'type' => $types['type'],
661
                'type_sql' => $col['Type']
662
            );
663
        }
664
665
        return $columnTypes;
666
    }
667
668
    /**
669
     * View structure extractor, create table (avoids cyclic references)
670
     *
671
     * @todo move mysql specific code to typeAdapter
672
     * @param string $viewName  Name of view to export
673
     * @return null
674
     */
675 View Code Duplication
    private function getViewStructureTable($viewName)
676
    {
677
        if (!$this->dumpSettings['skip-comments']) {
678
            $ret = "--" . PHP_EOL .
679
                "-- Stand-In structure for view `${viewName}`" . PHP_EOL .
680
                "--" . PHP_EOL . PHP_EOL;
681
            $this->compressManager->write($ret);
682
        }
683
        $stmt = $this->typeAdapter->show_create_view($viewName);
684
685
        // create views as tables, to resolve dependencies
686
        foreach ($this->dbHandler->query($stmt) as $r) {
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
687
            if ($this->dumpSettings['add-drop-table']) {
688
                $this->compressManager->write(
689
                    $this->typeAdapter->drop_view($viewName)
690
                );
691
            }
692
693
            $this->compressManager->write(
694
                $this->createStandInTable($viewName)
695
            );
696
            break;
697
        }
698
    }
699
700
    /**
701
     * Write a create table statement for the table Stand-In, show create
702
     * table would return a create algorithm when used on a view
703
     *
704
     * @param string $viewName  Name of view to export
705
     * @return string create statement
706
     */
707
    function createStandInTable($viewName) {
708
        $ret = array();
709
        foreach($this->tableColumnTypes[$viewName] as $k => $v) {
710
            $ret[] = "`${k}` ${v['type_sql']}";
711
        }
712
        $ret = implode(PHP_EOL . ",", $ret);
713
714
        $ret = "CREATE TABLE IF NOT EXISTS `$viewName` (" .
715
            PHP_EOL . $ret . PHP_EOL . ");" . PHP_EOL;
716
717
        return $ret;
718
    }
719
720
    /**
721
     * View structure extractor, create view
722
     *
723
     * @todo move mysql specific code to typeAdapter
724
     * @param string $viewName  Name of view to export
725
     * @return null
726
     */
727 View Code Duplication
    private function getViewStructureView($viewName)
728
    {
729
        if (!$this->dumpSettings['skip-comments']) {
730
            $ret = "--" . PHP_EOL .
731
                "-- View structure for view `${viewName}`" . PHP_EOL .
732
                "--" . PHP_EOL . PHP_EOL;
733
            $this->compressManager->write($ret);
734
        }
735
        $stmt = $this->typeAdapter->show_create_view($viewName);
736
737
        // create views, to resolve dependencies
738
        // replacing tables with views
739
        foreach ($this->dbHandler->query($stmt) as $r) {
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
740
            // because we must replace table with view, we should delete it
741
            $this->compressManager->write(
742
                $this->typeAdapter->drop_view($viewName)
743
            );
744
            $this->compressManager->write(
745
                $this->typeAdapter->create_view($r)
746
            );
747
            break;
748
        }
749
    }
750
751
    /**
752
     * Trigger structure extractor
753
     *
754
     * @param string $triggerName  Name of trigger to export
755
     * @return null
756
     */
757
    private function getTriggerStructure($triggerName)
758
    {
759
        $stmt = $this->typeAdapter->show_create_trigger($triggerName);
760
        foreach ($this->dbHandler->query($stmt) as $r) {
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
761
            if ($this->dumpSettings['add-drop-trigger']) {
762
                $this->compressManager->write(
763
                    $this->typeAdapter->add_drop_trigger($triggerName)
764
                );
765
            }
766
            $this->compressManager->write(
767
                $this->typeAdapter->create_trigger($r)
768
            );
769
            return;
770
        }
771
    }
772
773
    /**
774
     * Procedure structure extractor
775
     *
776
     * @param string $procedureName  Name of procedure to export
777
     * @return null
778
     */
779 View Code Duplication
    private function getProcedureStructure($procedureName)
780
    {
781
        if (!$this->dumpSettings['skip-comments']) {
782
            $ret = "--" . PHP_EOL .
783
                "-- Dumping routines for database '" . $this->dbName . "'" . PHP_EOL .
784
                "--" . PHP_EOL . PHP_EOL;
785
            $this->compressManager->write($ret);
786
        }
787
        $stmt = $this->typeAdapter->show_create_procedure($procedureName);
788
        foreach ($this->dbHandler->query($stmt) as $r) {
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
789
            $this->compressManager->write(
790
                $this->typeAdapter->create_procedure($r, $this->dumpSettings)
791
            );
792
            return;
793
        }
794
    }
795
796
    /**
797
     * Event structure extractor
798
     *
799
     * @param string $eventName  Name of event to export
800
     * @return null
801
     */
802 View Code Duplication
    private function getEventStructure($eventName)
803
    {
804
        if (!$this->dumpSettings['skip-comments']) {
805
            $ret = "--" . PHP_EOL .
806
                "-- Dumping events for database '" . $this->dbName . "'" . PHP_EOL .
807
                "--" . PHP_EOL . PHP_EOL;
808
            $this->compressManager->write($ret);
809
        }
810
        $stmt = $this->typeAdapter->show_create_event($eventName);
811
        foreach ($this->dbHandler->query($stmt) as $r) {
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
812
            $this->compressManager->write(
813
                $this->typeAdapter->create_event($r, $this->dumpSettings)
814
            );
815
            return;
816
        }
817
    }
818
819
    /**
820
     * Escape values with quotes when needed
821
     *
822
     * @param string $tableName Name of table which contains rows
823
     * @param array $row Associative array of column names and values to be quoted
824
     *
825
     * @return string
826
     */
827
    private function escape($tableName, $row)
828
    {
829
        $ret = array();
830
        $columnTypes = $this->tableColumnTypes[$tableName];
831
        foreach ($row as $colName => $colValue) {
832
            if (is_null($colValue)) {
833
                $ret[] = "NULL";
834
            } elseif ($this->dumpSettings['hex-blob'] && $columnTypes[$colName]['is_blob']) {
835
                if ($columnTypes[$colName]['type'] == 'bit' || !empty($colValue)) {
836
                    $ret[] = "0x${colValue}";
837
                } else {
838
                    $ret[] = "''";
839
                }
840
            } elseif ($columnTypes[$colName]['is_numeric']) {
841
                $ret[] = $colValue;
842
            } else {
843
                $ret[] = $this->dbHandler->quote($colValue);
0 ignored issues
show
Bug introduced by
The method quote cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
844
            }
845
        }
846
        return $ret;
847
    }
848
849
    /**
850
     * Table rows extractor
851
     *
852
     * @param string $tableName  Name of table to export
853
     *
854
     * @return null
855
     */
856
    private function listValues($tableName)
857
    {
858
        $this->prepareListValues($tableName);
859
860
        $onlyOnce = true;
861
        $lineSize = 0;
862
863
        $colStmt = $this->getColumnStmt($tableName);
864
        $stmt = "SELECT $colStmt FROM `$tableName`";
865
866
        if ($this->dumpSettings['where']) {
867
            $stmt .= " WHERE {$this->dumpSettings['where']}";
868
        }
869
        $resultSet = $this->dbHandler->query($stmt);
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
870
        $resultSet->setFetchMode(PDO::FETCH_ASSOC);
871
872
        foreach ($resultSet as $row) {
873
            $vals = $this->escape($tableName, $row);
874
            if ($onlyOnce || !$this->dumpSettings['extended-insert']) {
875
876
                if ($this->dumpSettings['complete-insert']) {
877
                    $lineSize += $this->compressManager->write(
878
                        "INSERT INTO `$tableName` (`" .
879
                        implode("`, `", array_keys($this->tableColumnTypes[$tableName])) .
880
                        "`) VALUES (" . implode(",", $vals) . ")"
881
                    );
882
                } else {
883
                    $lineSize += $this->compressManager->write(
884
                        "INSERT INTO `$tableName` VALUES (" . implode(",", $vals) . ")"
885
                    );
886
                }
887
                $onlyOnce = false;
888
            } else {
889
                $lineSize += $this->compressManager->write(",(" . implode(",", $vals) . ")");
890
            }
891
            if (($lineSize > $this->dumpSettings['net_buffer_length']) ||
892
                    !$this->dumpSettings['extended-insert']) {
893
                $onlyOnce = true;
894
                $lineSize = $this->compressManager->write(";" . PHP_EOL);
895
            }
896
        }
897
        $resultSet->closeCursor();
898
899
        if (!$onlyOnce) {
900
            $this->compressManager->write(";" . PHP_EOL);
901
        }
902
903
        $this->endListValues($tableName);
904
    }
905
906
    /**
907
     * Table rows extractor, append information prior to dump
908
     *
909
     * @param string $tableName  Name of table to export
910
     *
911
     * @return null
912
     */
913
    function prepareListValues($tableName)
914
    {
915
        if (!$this->dumpSettings['skip-comments']) {
916
            $this->compressManager->write(
917
                "--" . PHP_EOL .
918
                "-- Dumping data for table `$tableName`" .  PHP_EOL .
919
                "--" . PHP_EOL . PHP_EOL
920
            );
921
        }
922
923
        if ($this->dumpSettings['single-transaction']) {
924
            $this->dbHandler->exec($this->typeAdapter->setup_transaction());
0 ignored issues
show
Bug introduced by
The method exec cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
925
            $this->dbHandler->exec($this->typeAdapter->start_transaction());
0 ignored issues
show
Bug introduced by
The method exec cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
926
        }
927
928
        if ($this->dumpSettings['lock-tables']) {
929
            $this->typeAdapter->lock_table($tableName);
930
        }
931
932
        if ($this->dumpSettings['add-locks']) {
933
            $this->compressManager->write(
934
                $this->typeAdapter->start_add_lock_table($tableName)
935
            );
936
        }
937
938
        if ($this->dumpSettings['disable-keys']) {
939
            $this->compressManager->write(
940
                $this->typeAdapter->start_add_disable_keys($tableName)
941
            );
942
        }
943
944
        // Disable autocommit for faster reload
945
        if ($this->dumpSettings['no-autocommit']) {
946
            $this->compressManager->write(
947
                $this->typeAdapter->start_disable_autocommit()
948
            );
949
        }
950
951
        return;
952
    }
953
954
    /**
955
     * Table rows extractor, close locks and commits after dump
956
     *
957
     * @param string $tableName  Name of table to export
958
     *
959
     * @return null
960
     */
961
    function endListValues($tableName)
962
    {
963
        if ($this->dumpSettings['disable-keys']) {
964
            $this->compressManager->write(
965
                $this->typeAdapter->end_add_disable_keys($tableName)
966
            );
967
        }
968
969
        if ($this->dumpSettings['add-locks']) {
970
            $this->compressManager->write(
971
                $this->typeAdapter->end_add_lock_table($tableName)
972
            );
973
        }
974
975
        if ($this->dumpSettings['single-transaction']) {
976
            $this->dbHandler->exec($this->typeAdapter->commit_transaction());
0 ignored issues
show
Bug introduced by
The method exec cannot be called on $this->dbHandler (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
977
        }
978
979
        if ($this->dumpSettings['lock-tables']) {
980
            $this->typeAdapter->unlock_table($tableName);
981
        }
982
983
        // Commit to enable autocommit
984
        if ($this->dumpSettings['no-autocommit']) {
985
            $this->compressManager->write(
986
                $this->typeAdapter->end_disable_autocommit()
987
            );
988
        }
989
990
        $this->compressManager->write(PHP_EOL);
991
992
        return;
993
    }
994
995
    /**
996
     * Build SQL List of all columns on current table
997
     *
998
     * @param string $tableName  Name of table to get columns
999
     *
1000
     * @return string SQL sentence with columns
1001
     */
1002
    function getColumnStmt($tableName)
1003
    {
1004
        $colStmt = array();
1005
        foreach($this->tableColumnTypes[$tableName] as $colName => $colType) {
1006
            if ($colType['type'] == 'bit' && $this->dumpSettings['hex-blob']) {
1007
                $colStmt[] = "LPAD(HEX(`${colName}`),2,'0') AS `${colName}`";
1008
            } else if ($colType['is_blob'] && $this->dumpSettings['hex-blob']) {
1009
                $colStmt[] = "HEX(`${colName}`) AS `${colName}`";
1010
            } else {
1011
                $colStmt[] = "`${colName}`";
1012
            }
1013
        }
1014
        $colStmt = implode($colStmt, ",");
1015
1016
        return $colStmt;
1017
    }
1018
}
1019
1020
/**
1021
 * Enum with all available compression methods
1022
 *
1023
 */
1024
abstract class CompressMethod
1025
{
1026
    public static $enums = array(
1027
        "None",
1028
        "Gzip",
1029
        "Bzip2"
1030
    );
1031
1032
    /**
1033
     * @param string $c
1034
     * @return boolean
1035
     */
1036
    public static function isValid($c)
1037
    {
1038
        return in_array($c, self::$enums);
1039
    }
1040
}
1041
1042
abstract class CompressManagerFactory
1043
{
1044
    /**
1045
     * @param string $c
1046
     * @return CompressBzip2|CompressGzip|CompressNone
1047
     */
1048 View Code Duplication
    public static function create($c)
1049
    {
1050
        $c = ucfirst(strtolower($c));
1051
        if (! CompressMethod::isValid($c)) {
1052
            throw new Exception("Compression method ($c) is not defined yet");
1053
        }
1054
1055
        $method =  __NAMESPACE__ . "\\" . "Compress" . $c;
1056
1057
        return new $method;
1058
    }
1059
}
1060
1061 View Code Duplication
class CompressBzip2 extends CompressManagerFactory
1062
{
1063
    private $fileHandler = null;
1064
1065
    public function __construct()
1066
    {
1067
        if (! function_exists("bzopen")) {
1068
            throw new Exception("Compression is enabled, but bzip2 lib is not installed or configured properly");
1069
        }
1070
    }
1071
1072
    /**
1073
     * @param string $filename
1074
     */
1075
    public function open($filename)
1076
    {
1077
        $this->fileHandler = bzopen($filename, "w");
1078
        if (false === $this->fileHandler) {
1079
            throw new Exception("Output file is not writable");
1080
        }
1081
1082
        return true;
1083
    }
1084
1085
    public function write($str)
1086
    {
1087
        if (false === ($bytesWritten = bzwrite($this->fileHandler, $str))) {
1088
            throw new Exception("Writting to file failed! Probably, there is no more free space left?");
1089
        }
1090
        return $bytesWritten;
1091
    }
1092
1093
    public function close()
1094
    {
1095
        return bzclose($this->fileHandler);
1096
    }
1097
}
1098
1099 View Code Duplication
class CompressGzip extends CompressManagerFactory
1100
{
1101
    private $fileHandler = null;
1102
1103
    public function __construct()
1104
    {
1105
        if (! function_exists("gzopen")) {
1106
            throw new Exception("Compression is enabled, but gzip lib is not installed or configured properly");
1107
        }
1108
    }
1109
1110
    /**
1111
     * @param string $filename
1112
     */
1113
    public function open($filename)
1114
    {
1115
        $this->fileHandler = gzopen($filename, "wb");
1116
        if (false === $this->fileHandler) {
1117
            throw new Exception("Output file is not writable");
1118
        }
1119
1120
        return true;
1121
    }
1122
1123
    public function write($str)
1124
    {
1125
        if (false === ($bytesWritten = gzwrite($this->fileHandler, $str))) {
1126
            throw new Exception("Writting to file failed! Probably, there is no more free space left?");
1127
        }
1128
        return $bytesWritten;
1129
    }
1130
1131
    public function close()
1132
    {
1133
        return gzclose($this->fileHandler);
1134
    }
1135
}
1136
1137
class CompressNone extends CompressManagerFactory
1138
{
1139
    private $fileHandler = null;
1140
1141
    /**
1142
     * @param string $filename
1143
     */
1144
    public function open($filename)
1145
    {
1146
        $this->fileHandler = fopen($filename, "wb");
1147
        if (false === $this->fileHandler) {
1148
            throw new Exception("Output file is not writable");
1149
        }
1150
1151
        return true;
1152
    }
1153
1154
    public function write($str)
1155
    {
1156
        if (false === ($bytesWritten = fwrite($this->fileHandler, $str))) {
1157
            throw new Exception("Writting to file failed! Probably, there is no more free space left?");
1158
        }
1159
        return $bytesWritten;
1160
    }
1161
1162
    public function close()
1163
    {
1164
        return fclose($this->fileHandler);
1165
    }
1166
}
1167
1168
/**
1169
 * Enum with all available TypeAdapter implementations
1170
 *
1171
 */
1172
abstract class TypeAdapter
1173
{
1174
    public static $enums = array(
1175
        "Sqlite",
1176
        "Mysql"
1177
    );
1178
1179
    /**
1180
     * @param string $c
1181
     * @return boolean
1182
     */
1183
    public static function isValid($c)
1184
    {
1185
        return in_array($c, self::$enums);
1186
    }
1187
}
1188
1189
/**
1190
 * TypeAdapter Factory
1191
 *
1192
 */
1193
abstract class TypeAdapterFactory
1194
{
1195
    /**
1196
     * @param string $c Type of database factory to create (Mysql, Sqlite,...)
1197
     * @param PDO $dbHandler
1198
     */
1199 View Code Duplication
    public static function create($c, $dbHandler = null)
1200
    {
1201
        $c = ucfirst(strtolower($c));
1202
        if (! TypeAdapter::isValid($c)) {
1203
            throw new Exception("Database type support for ($c) not yet available");
1204
        }
1205
        $method =  __NAMESPACE__ . "\\" . "TypeAdapter" . $c;
1206
        return new $method($dbHandler);
1207
    }
1208
1209
    /**
1210
     * function databases Add sql to create and use database
1211
     * @todo make it do something with sqlite
1212
     */
1213
    public function databases()
1214
    {
1215
        return "";
1216
    }
1217
1218
    public function show_create_table($tableName)
1219
    {
1220
        return "SELECT tbl_name as 'Table', sql as 'Create Table' " .
1221
            "FROM sqlite_master " .
1222
            "WHERE type='table' AND tbl_name='$tableName'";
1223
    }
1224
1225
    /**
1226
     * function create_table Get table creation code from database
1227
     * @todo make it do something with sqlite
1228
     */
1229
    public function create_table($row, $dumpSettings)
1230
    {
1231
        return "";
1232
    }
1233
1234
    public function show_create_view($viewName)
1235
    {
1236
        return "SELECT tbl_name as 'View', sql as 'Create View' " .
1237
            "FROM sqlite_master " .
1238
            "WHERE type='view' AND tbl_name='$viewName'";
1239
    }
1240
1241
    /**
1242
     * function create_view Get view creation code from database
1243
     * @todo make it do something with sqlite
1244
     */
1245
    public function create_view($row)
1246
    {
1247
        return "";
1248
    }
1249
1250
    /**
1251
     * function show_create_trigger Get trigger creation code from database
1252
     * @todo make it do something with sqlite
1253
     */
1254
    public function show_create_trigger($triggerName)
1255
    {
1256
        return "";
1257
    }
1258
1259
    /**
1260
     * function create_trigger Modify trigger code, add delimiters, etc
1261
     * @todo make it do something with sqlite
1262
     */
1263
    public function create_trigger($triggerName)
1264
    {
1265
        return "";
1266
    }
1267
1268
    /**
1269
     * function create_procedure Modify procedure code, add delimiters, etc
1270
     * @todo make it do something with sqlite
1271
     */
1272
    public function create_procedure($procedureName, $dumpSettings)
1273
    {
1274
        return "";
1275
    }
1276
1277
    public function show_tables()
1278
    {
1279
        return "SELECT tbl_name FROM sqlite_master WHERE type='table'";
1280
    }
1281
1282
    public function show_views()
1283
    {
1284
        return "SELECT tbl_name FROM sqlite_master WHERE type='view'";
1285
    }
1286
1287
    public function show_triggers()
1288
    {
1289
        return "SELECT name FROM sqlite_master WHERE type='trigger'";
1290
    }
1291
1292
    public function show_columns()
1293
    {
1294
        if (func_num_args() != 1) {
1295
            return "";
1296
        }
1297
1298
        $args = func_get_args();
1299
1300
        return "pragma table_info(${args[0]})";
1301
    }
1302
1303
    public function show_procedures()
1304
    {
1305
        return "";
1306
    }
1307
1308
    public function show_events()
1309
    {
1310
        return "";
1311
    }
1312
1313
    public function setup_transaction()
1314
    {
1315
        return "";
1316
    }
1317
1318
    public function start_transaction()
1319
    {
1320
        return "BEGIN EXCLUSIVE";
1321
    }
1322
1323
    public function commit_transaction()
1324
    {
1325
        return "COMMIT";
1326
    }
1327
1328
    public function lock_table()
1329
    {
1330
        return "";
1331
    }
1332
1333
    public function unlock_table()
1334
    {
1335
        return "";
1336
    }
1337
1338
    public function start_add_lock_table()
1339
    {
1340
        return PHP_EOL;
1341
    }
1342
1343
    public function end_add_lock_table()
1344
    {
1345
        return PHP_EOL;
1346
    }
1347
1348
    public function start_add_disable_keys()
1349
    {
1350
        return PHP_EOL;
1351
    }
1352
1353
    public function end_add_disable_keys()
1354
    {
1355
        return PHP_EOL;
1356
    }
1357
1358
    public function start_disable_foreign_keys_check()
1359
    {
1360
        return PHP_EOL;
1361
    }
1362
1363
    public function end_disable_foreign_keys_check()
1364
    {
1365
        return PHP_EOL;
1366
    }
1367
1368
    public function add_drop_database()
1369
    {
1370
        return PHP_EOL;
1371
    }
1372
1373
    public function add_drop_trigger()
1374
    {
1375
        return PHP_EOL;
1376
    }
1377
1378
    public function drop_table()
1379
    {
1380
        return PHP_EOL;
1381
    }
1382
1383
    public function drop_view()
1384
    {
1385
        return PHP_EOL;
1386
    }
1387
1388
    /**
1389
     * Decode column metadata and fill info structure.
1390
     * type, is_numeric and is_blob will always be available.
1391
     *
1392
     * @param array $colType Array returned from "SHOW COLUMNS FROM tableName"
1393
     * @return array
1394
     */
1395
    public function parseColumnType($colType)
1396
    {
1397
        return array();
1398
    }
1399
1400
    public function backup_parameters()
1401
    {
1402
        return PHP_EOL;
1403
    }
1404
1405
    public function restore_parameters()
1406
    {
1407
        return PHP_EOL;
1408
    }
1409
}
1410
1411
class TypeAdapterPgsql extends TypeAdapterFactory
1412
{
1413
}
1414
1415
class TypeAdapterDblib extends TypeAdapterFactory
1416
{
1417
}
1418
1419
class TypeAdapterSqlite extends TypeAdapterFactory
1420
{
1421
}
1422
1423
class TypeAdapterMysql extends TypeAdapterFactory
1424
{
1425
1426
    private $dbHandler = null;
1427
1428
    // Numerical Mysql types
1429
    public $mysqlTypes = array(
1430
        'numerical' => array(
1431
            'bit',
1432
            'tinyint',
1433
            'smallint',
1434
            'mediumint',
1435
            'int',
1436
            'integer',
1437
            'bigint',
1438
            'real',
1439
            'double',
1440
            'float',
1441
            'decimal',
1442
            'numeric'
1443
        ),
1444
        'blob' => array(
1445
            'tinyblob',
1446
            'blob',
1447
            'mediumblob',
1448
            'longblob',
1449
            'binary',
1450
            'varbinary',
1451
            'bit',
1452
            'geometry', /* http://bugs.mysql.com/bug.php?id=43544 */
1453
            'point',
1454
            'linestring',
1455
            'polygon',
1456
            'multipoint',
1457
            'multilinestring',
1458
            'multipolygon',
1459
            'geometrycollection',
1460
        )
1461
    );
1462
1463
    public function __construct ($dbHandler)
1464
    {
1465
        $this->dbHandler = $dbHandler;
1466
    }
1467
1468
    public function databases()
1469
    {
1470
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1471
        $args = func_get_args();
1472
        $databaseName = $args[0];
1473
1474
        $resultSet = $this->dbHandler->query("SHOW VARIABLES LIKE 'character_set_database';");
1475
        $characterSet = $resultSet->fetchColumn(1);
1476
        $resultSet->closeCursor();
1477
1478
        $resultSet = $this->dbHandler->query("SHOW VARIABLES LIKE 'collation_database';");
1479
        $collationDb = $resultSet->fetchColumn(1);
1480
        $resultSet->closeCursor();
1481
        $ret = "";
1482
1483
        $ret .= "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `${databaseName}`".
1484
            " /*!40100 DEFAULT CHARACTER SET ${characterSet} " .
1485
            " COLLATE ${collationDb} */;" . PHP_EOL . PHP_EOL .
1486
            "USE `${databaseName}`;" . PHP_EOL . PHP_EOL;
1487
1488
        return $ret;
1489
    }
1490
1491
    public function show_create_table($tableName)
1492
    {
1493
        return "SHOW CREATE TABLE `$tableName`";
1494
    }
1495
1496
    public function show_create_view($viewName)
1497
    {
1498
        return "SHOW CREATE VIEW `$viewName`";
1499
    }
1500
1501
    public function show_create_trigger($triggerName)
1502
    {
1503
        return "SHOW CREATE TRIGGER `$triggerName`";
1504
    }
1505
1506
    public function show_create_procedure($procedureName)
1507
    {
1508
        return "SHOW CREATE PROCEDURE `$procedureName`";
1509
    }
1510
1511
    public function show_create_event($eventName)
1512
    {
1513
        return "SHOW CREATE EVENT `$eventName`";
1514
    }
1515
1516
    public function create_table( $row, $dumpSettings )
1517
    {
1518
        if ( !isset($row['Create Table']) ) {
1519
            throw new Exception("Error getting table code, unknown output");
1520
        }
1521
1522
        $createTable = $row['Create Table'];
1523
        if ( $dumpSettings['reset-auto-increment'] ) {
1524
            $match = "/AUTO_INCREMENT=[0-9]+/s";
1525
            $replace = "";
1526
            $createTable = preg_replace($match, $replace, $createTable);
1527
        }
1528
1529
        $ret = "/*!40101 SET @saved_cs_client     = @@character_set_client */;" . PHP_EOL .
1530
            "/*!40101 SET character_set_client = " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL .
1531
            $createTable . ";" . PHP_EOL .
1532
            "/*!40101 SET character_set_client = @saved_cs_client */;" . PHP_EOL .
1533
            PHP_EOL;
1534
        return $ret;
1535
    }
1536
1537
    public function create_view($row)
1538
    {
1539
        $ret = "";
1540
        if (!isset($row['Create View'])) {
1541
                throw new Exception("Error getting view structure, unknown output");
1542
        }
1543
1544
        $triggerStmt = $row['Create View'];
1545
1546
        $triggerStmtReplaced1 = str_replace(
1547
            "CREATE ALGORITHM",
1548
            "/*!50001 CREATE ALGORITHM",
1549
            $triggerStmt
1550
        );
1551
        $triggerStmtReplaced2 = str_replace(
1552
            " DEFINER=",
1553
            " */" . PHP_EOL . "/*!50013 DEFINER=",
1554
            $triggerStmtReplaced1
1555
        );
1556
        $triggerStmtReplaced3 = str_replace(
1557
            " VIEW ",
1558
            " */" . PHP_EOL . "/*!50001 VIEW ",
1559
            $triggerStmtReplaced2
1560
        );
1561
        if (false === $triggerStmtReplaced1 ||
1562
            false === $triggerStmtReplaced2 ||
1563
            false === $triggerStmtReplaced3) {
1564
            $triggerStmtReplaced = $triggerStmt;
1565
        } else {
1566
            $triggerStmtReplaced = $triggerStmtReplaced3 . " */;";
1567
        }
1568
1569
        $ret .= $triggerStmtReplaced . PHP_EOL . PHP_EOL;
1570
        return $ret;
1571
    }
1572
1573
    public function create_trigger($row)
1574
    {
1575
        $ret = "";
1576
        if (!isset($row['SQL Original Statement'])) {
1577
            throw new Exception("Error getting trigger code, unknown output");
1578
        }
1579
1580
        $triggerStmt = $row['SQL Original Statement'];
1581
        $triggerStmtReplaced = str_replace(
1582
            "CREATE DEFINER",
1583
            "/*!50003 CREATE*/ /*!50017 DEFINER",
1584
            $triggerStmt
1585
        );
1586
        $triggerStmtReplaced = str_replace(
1587
            " TRIGGER",
1588
            "*/ /*!50003 TRIGGER",
1589
            $triggerStmtReplaced
1590
        );
1591
        if ( false === $triggerStmtReplaced ) {
1592
            $triggerStmtReplaced = $triggerStmt . " /* ";
1593
        }
1594
1595
        $ret .= "DELIMITER ;;" . PHP_EOL .
1596
            $triggerStmtReplaced . " */ ;;" . PHP_EOL .
1597
            "DELIMITER ;" . PHP_EOL . PHP_EOL;
1598
        return $ret;
1599
    }
1600
1601
    public function create_procedure($row, $dumpSettings)
1602
    {
1603
        $ret = "";
1604
        if (!isset($row['Create Procedure'])) {
1605
            throw new Exception("Error getting procedure code, unknown output. " .
1606
                "Please check 'https://bugs.mysql.com/bug.php?id=14564'");
1607
        }
1608
        $procedureStmt = $row['Create Procedure'];
1609
1610
        $ret .= "/*!50003 DROP PROCEDURE IF EXISTS `" .
1611
            $row['Procedure'] . "` */;" . PHP_EOL .
1612
            "/*!40101 SET @saved_cs_client     = @@character_set_client */;" . PHP_EOL .
1613
            "/*!40101 SET character_set_client = " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL .
1614
            "DELIMITER ;;" . PHP_EOL .
1615
            $procedureStmt . " ;;" . PHP_EOL .
1616
            "DELIMITER ;" . PHP_EOL .
1617
            "/*!40101 SET character_set_client = @saved_cs_client */;" . PHP_EOL . PHP_EOL;
1618
1619
        return $ret;
1620
    }
1621
1622
    public function create_event($row, $dumpSettings)
0 ignored issues
show
Unused Code introduced by
The parameter $dumpSettings is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1623
    {
1624
        $ret = "";
1625
        if ( !isset($row['Create Event']) ) {
1626
            throw new Exception("Error getting event code, unknown output. " .
1627
                "Please check 'http://stackoverflow.com/questions/10853826/mysql-5-5-create-event-gives-syntax-error'");
1628
        }
1629
        $eventName = $row['Event'];
1630
        $eventStmt = $row['Create Event'];
1631
        $sqlMode = $row['sql_mode'];
1632
1633
        $eventStmtReplaced = str_replace(
1634
            "CREATE DEFINER",
1635
            "/*!50106 CREATE*/ /*!50117 DEFINER",
1636
            $eventStmt
1637
        );
1638
        $eventStmtReplaced = str_replace(
1639
            " EVENT ",
1640
            "*/ /*!50106 EVENT ",
1641
            $eventStmtReplaced
1642
        );
1643
1644
        if ( false === $eventStmtReplaced ) {
1645
            $eventStmtReplaced = $eventStmt . " /* ";
1646
        }
1647
1648
        $ret .= "/*!50106 SET @save_time_zone= @@TIME_ZONE */ ;" . PHP_EOL .
1649
            "/*!50106 DROP EVENT IF EXISTS `" . $eventName . "` */;" . PHP_EOL .
1650
            "DELIMITER ;;" . PHP_EOL .
1651
            "/*!50003 SET @saved_cs_client      = @@character_set_client */ ;;" . PHP_EOL .
1652
            "/*!50003 SET @saved_cs_results     = @@character_set_results */ ;;" . PHP_EOL .
1653
            "/*!50003 SET @saved_col_connection = @@collation_connection */ ;;" . PHP_EOL .
1654
            "/*!50003 SET character_set_client  = utf8 */ ;;" . PHP_EOL .
1655
            "/*!50003 SET character_set_results = utf8 */ ;;" . PHP_EOL .
1656
            "/*!50003 SET collation_connection  = utf8_general_ci */ ;;" . PHP_EOL .
1657
            "/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;;" . PHP_EOL .
1658
            "/*!50003 SET sql_mode              = '" . $sqlMode . "' */ ;;" . PHP_EOL .
1659
            "/*!50003 SET @saved_time_zone      = @@time_zone */ ;;" . PHP_EOL .
1660
            "/*!50003 SET time_zone             = 'SYSTEM' */ ;;" . PHP_EOL .
1661
            $eventStmtReplaced . " */ ;;" . PHP_EOL .
1662
            "/*!50003 SET time_zone             = @saved_time_zone */ ;;" . PHP_EOL .
1663
            "/*!50003 SET sql_mode              = @saved_sql_mode */ ;;" . PHP_EOL .
1664
            "/*!50003 SET character_set_client  = @saved_cs_client */ ;;" . PHP_EOL .
1665
            "/*!50003 SET character_set_results = @saved_cs_results */ ;;" . PHP_EOL .
1666
            "/*!50003 SET collation_connection  = @saved_col_connection */ ;;" . PHP_EOL .
1667
            "DELIMITER ;" . PHP_EOL .
1668
            "/*!50106 SET TIME_ZONE= @save_time_zone */ ;" . PHP_EOL . PHP_EOL;
1669
            // Commented because we are doing this in restore_parameters()
1670
            // "/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;" . PHP_EOL . PHP_EOL;
1671
1672
        return $ret;
1673
    }
1674
1675 View Code Duplication
    public function show_tables()
1676
    {
1677
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1678
        $args = func_get_args();
1679
        return "SELECT TABLE_NAME AS tbl_name " .
1680
            "FROM INFORMATION_SCHEMA.TABLES " .
1681
            "WHERE TABLE_TYPE='BASE TABLE' AND TABLE_SCHEMA='${args[0]}'";
1682
    }
1683
1684 View Code Duplication
    public function show_views()
1685
    {
1686
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1687
        $args = func_get_args();
1688
        return "SELECT TABLE_NAME AS tbl_name " .
1689
            "FROM INFORMATION_SCHEMA.TABLES " .
1690
            "WHERE TABLE_TYPE='VIEW' AND TABLE_SCHEMA='${args[0]}'";
1691
    }
1692
1693
    public function show_triggers()
1694
    {
1695
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1696
        $args = func_get_args();
1697
        return "SHOW TRIGGERS FROM `${args[0]}`;";
1698
    }
1699
1700
    public function show_columns()
1701
    {
1702
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1703
        $args = func_get_args();
1704
        return "SHOW COLUMNS FROM `${args[0]}`;";
1705
    }
1706
1707 View Code Duplication
    public function show_procedures()
1708
    {
1709
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1710
        $args = func_get_args();
1711
        return "SELECT SPECIFIC_NAME AS procedure_name " .
1712
            "FROM INFORMATION_SCHEMA.ROUTINES " .
1713
            "WHERE ROUTINE_TYPE='PROCEDURE' AND ROUTINE_SCHEMA='${args[0]}'";
1714
    }
1715
1716
    /**
1717
     * Get query string to ask for names of events from current database.
1718
     *
1719
     * @param string Name of database
1720
     * @return string
1721
     */
1722 View Code Duplication
    public function show_events()
1723
    {
1724
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1725
        $args = func_get_args();
1726
        return "SELECT EVENT_NAME AS event_name " .
1727
            "FROM INFORMATION_SCHEMA.EVENTS " .
1728
            "WHERE EVENT_SCHEMA='${args[0]}'";
1729
    }
1730
1731
    public function setup_transaction()
1732
    {
1733
        return "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ";
1734
    }
1735
1736
    public function start_transaction()
1737
    {
1738
        return "START TRANSACTION";
1739
    }
1740
1741
    public function commit_transaction()
1742
    {
1743
        return "COMMIT";
1744
    }
1745
1746
    public function lock_table()
1747
    {
1748
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1749
        $args = func_get_args();
1750
        return $this->dbHandler->exec("LOCK TABLES `${args[0]}` READ LOCAL");
1751
1752
    }
1753
1754
    public function unlock_table()
1755
    {
1756
        return $this->dbHandler->exec("UNLOCK TABLES");
1757
    }
1758
1759
    public function start_add_lock_table()
1760
    {
1761
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1762
        $args = func_get_args();
1763
        return "LOCK TABLES `${args[0]}` WRITE;" . PHP_EOL;
1764
    }
1765
1766
    public function end_add_lock_table()
1767
    {
1768
        return "UNLOCK TABLES;" . PHP_EOL;
1769
    }
1770
1771 View Code Duplication
    public function start_add_disable_keys()
1772
    {
1773
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1774
        $args = func_get_args();
1775
        return "/*!40000 ALTER TABLE `${args[0]}` DISABLE KEYS */;" .
1776
            PHP_EOL;
1777
    }
1778
1779 View Code Duplication
    public function end_add_disable_keys()
1780
    {
1781
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1782
        $args = func_get_args();
1783
        return "/*!40000 ALTER TABLE `${args[0]}` ENABLE KEYS */;" .
1784
            PHP_EOL;
1785
    }
1786
1787
    public function start_disable_autocommit()
1788
    {
1789
        return "SET autocommit=0;" . PHP_EOL;
1790
    }
1791
1792
    public function end_disable_autocommit()
1793
    {
1794
        return "COMMIT;" . PHP_EOL;
1795
    }
1796
1797 View Code Duplication
    public function add_drop_database()
1798
    {
1799
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1800
        $args = func_get_args();
1801
        return "/*!40000 DROP DATABASE IF EXISTS `${args[0]}`*/;" .
1802
            PHP_EOL . PHP_EOL;
1803
    }
1804
1805
    public function add_drop_trigger()
1806
    {
1807
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1808
        $args = func_get_args();
1809
        return "DROP TRIGGER IF EXISTS `${args[0]}`;" . PHP_EOL;
1810
    }
1811
1812
    public function drop_table()
1813
    {
1814
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1815
        $args = func_get_args();
1816
        return "DROP TABLE IF EXISTS `${args[0]}`;" . PHP_EOL;
1817
    }
1818
1819 View Code Duplication
    public function drop_view()
1820
    {
1821
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1822
        $args = func_get_args();
1823
        return "DROP TABLE IF EXISTS `${args[0]}`;" . PHP_EOL .
1824
                "/*!50001 DROP VIEW IF EXISTS `${args[0]}`*/;" . PHP_EOL;
1825
    }
1826
1827
    public function getDatabaseHeader()
1828
    {
1829
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1830
        $args = func_get_args();
1831
        return "--" . PHP_EOL .
1832
            "-- Current Database: `${args[0]}`" . PHP_EOL .
1833
            "--" . PHP_EOL . PHP_EOL;
1834
    }
1835
1836
    /**
1837
     * Decode column metadata and fill info structure.
1838
     * type, is_numeric and is_blob will always be available.
1839
     *
1840
     * @param array $colType Array returned from "SHOW COLUMNS FROM tableName"
1841
     * @return array
1842
     */
1843
    public function parseColumnType($colType)
1844
    {
1845
        $colInfo = array();
1846
        $colParts = explode(" ", $colType['Type']);
1847
1848
        if($fparen = strpos($colParts[0], "("))
1849
        {
1850
            $colInfo['type'] = substr($colParts[0], 0, $fparen);
1851
            $colInfo['length']  = str_replace(")", "", substr($colParts[0], $fparen+1));
1852
            $colInfo['attributes'] = isset($colParts[1]) ? $colParts[1] : NULL;
1853
        }
1854
        else
1855
        {
1856
            $colInfo['type'] = $colParts[0];
1857
        }
1858
        $colInfo['is_numeric'] = in_array($colInfo['type'], $this->mysqlTypes['numerical']);
1859
        $colInfo['is_blob'] = in_array($colInfo['type'], $this->mysqlTypes['blob']);
1860
1861
        return $colInfo;
1862
    }
1863
1864
    public function backup_parameters()
1865
    {
1866
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1867
        $args = func_get_args();
1868
        $dumpSettings = $args[0];
1869
        $ret = "/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;" . PHP_EOL .
1870
            "/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;" . PHP_EOL .
1871
            "/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;" . PHP_EOL .
1872
            "/*!40101 SET NAMES " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL;
1873
1874
        if (false === $dumpSettings['skip-tz-utc']) {
1875
            $ret .= "/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;" . PHP_EOL .
1876
                "/*!40103 SET TIME_ZONE='+00:00' */;" . PHP_EOL;
1877
        }
1878
1879
        $ret .= "/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;" . PHP_EOL .
1880
            "/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;" . PHP_EOL .
1881
            "/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;" . PHP_EOL .
1882
            "/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;" . PHP_EOL .PHP_EOL;
1883
1884
        return $ret;
1885
    }
1886
1887
    public function restore_parameters()
1888
    {
1889
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1890
        $args = func_get_args();
1891
        $dumpSettings = $args[0];
1892
        $ret = "";
1893
1894
        if (false === $dumpSettings['skip-tz-utc']) {
1895
            $ret .= "/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;" . PHP_EOL;
1896
        }
1897
1898
        $ret .= "/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;" . PHP_EOL .
1899
            "/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;" . PHP_EOL .
1900
            "/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;" . PHP_EOL .
1901
            "/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;" . PHP_EOL .
1902
            "/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;" . PHP_EOL .
1903
            "/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;" . PHP_EOL .
1904
            "/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;" . PHP_EOL . PHP_EOL;
1905
1906
        return $ret;
1907
    }
1908
1909
    /**
1910
     * Check number of parameters passed to function, useful when inheriting.
1911
     * Raise exception if unexpected.
1912
     *
1913
     * @param integer $num_args
1914
     * @param integer $expected_num_args
1915
     * @param string $method_name
1916
     */
1917
    private function check_parameters($num_args, $expected_num_args, $method_name)
1918
    {
1919
        if ( $num_args != $expected_num_args ) {
1920
            throw new Exception("Unexpected parameter passed to $method_name");
1921
        }
1922
        return;
1923
    }
1924
}
1925