Completed
Pull Request — master (#117)
by
unknown
04:46
created

Mysqldump::setConnection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 2
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
        if ($dsn !== '') {
158
            $this->parseDsn($dsn);
159
        }
160
        $this->pdoSettings = self::array_replace_recursive($pdoSettingsDefault, $pdoSettings);
161
        $this->dumpSettings = self::array_replace_recursive($dumpSettingsDefault, $dumpSettings);
162
163
        $this->dumpSettings['init_commands'][] = "SET NAMES " . $this->dumpSettings['default-character-set'];
164
165
        if (false === $this->dumpSettings['skip-tz-utc']) {
166
            $this->dumpSettings['init_commands'][] = "SET TIME_ZONE='+00:00'";
167
        }
168
169
        $diff = array_diff(array_keys($this->dumpSettings), array_keys($dumpSettingsDefault));
170
        if (count($diff)>0) {
171
            throw new Exception("Unexpected value in dumpSettings: (" . implode(",", $diff) . ")");
172
        }
173
174
        if ( !is_array($this->dumpSettings['include-tables']) ||
175
            !is_array($this->dumpSettings['exclude-tables']) ) {
176
            throw new Exception("Include-tables and exclude-tables should be arrays");
177
        }
178
179
        // Dump the same views as tables, mimic mysqldump behaviour
180
        $this->dumpSettings['include-views'] = $this->dumpSettings['include-tables'];
181
182
        // Create a new compressManager to manage compressed output
183
        $this->compressManager = CompressManagerFactory::create($this->dumpSettings['compress']);
184
    }
185
186
    /**
187
     * Destructor of Mysqldump. Unsets dbHandlers and database objects.
188
     *
189
     */
190
    public function __destruct()
191
    {
192
        $this->dbHandler = null;
193
    }
194
195
    /**
196
     * Custom array_replace_recursive to be used if PHP < 5.3
197
     * Replaces elements from passed arrays into the first array recursively
198
     *
199
     * @param array $array1 The array in which elements are replaced
200
     * @param array $array2 The array from which elements will be extracted
201
     *
202
     * @return array Returns an array, or NULL if an error occurs.
203
     */
204
    public static function array_replace_recursive($array1, $array2)
205
    {
206
        if (function_exists('array_replace_recursive')) {
207
            return array_replace_recursive($array1, $array2);
208
        }
209
210
        foreach ($array2 as $key => $value) {
211
            if (is_array($value)) {
212
                $array1[$key] = self::array_replace_recursive($array1[$key], $value);
213
            } else {
214
                $array1[$key] = $value;
215
            }
216
        }
217
        return $array1;
218
    }
219
220
    /**
221
     * Parse DSN string and extract dbname value
222
     * Several examples of a DSN string
223
     *   mysql:host=localhost;dbname=testdb
224
     *   mysql:host=localhost;port=3307;dbname=testdb
225
     *   mysql:unix_socket=/tmp/mysql.sock;dbname=testdb
226
     *
227
     * @param string $dsn dsn string to parse
228
     */
229
    private function parseDsn($dsn)
230
    {
231
        if (empty($dsn) || (false === ($pos = strpos($dsn, ":")))) {
232
            throw new Exception("Empty DSN string");
233
        }
234
235
        $this->dsn = $dsn;
236
        $this->dbType = strtolower(substr($dsn, 0, $pos));
237
238
        if (empty($this->dbType)) {
239
            throw new Exception("Missing database type from DSN string");
240
        }
241
242
        $dsn = substr($dsn, $pos + 1);
243
244
        foreach(explode(";", $dsn) as $kvp) {
245
            $kvpArr = explode("=", $kvp);
246
            $this->dsnArray[strtolower($kvpArr[0])] = $kvpArr[1];
247
        }
248
249
        if (empty($this->dsnArray['host']) &&
250
            empty($this->dsnArray['unix_socket'])) {
251
            throw new Exception("Missing host from DSN string");
252
        }
253
        $this->host = (!empty($this->dsnArray['host'])) ?
254
            $this->dsnArray['host'] :
255
            $this->dsnArray['unix_socket'];
256
257
        if (empty($this->dsnArray['dbname'])) {
258
            throw new Exception("Missing database name from DSN string");
259
        }
260
261
        $this->dbName = $this->dsnArray['dbname'];
262
263
        return true;
264
    }
265
266
    /**
267
     * Register existing connection.
268
     *
269
     * @param $dbType - Connection type.
270
     * @param $dbHandler - PDO connection Resource.
271
     */
272
    public function setConnection($dbType, $dbHandler){
273
        $this->dbType = $dbType;
274
        $this->dbHandler = $dbHandler;
275
    }
276
277
    /**
278
     * Connect with PDO
279
     *
280
     * @return null
281
     */
282
    private function connect()
283
    {
284
        // Connecting with PDO
285
        try {
286
            if(!isset($this->dbHandler)) {
287
                switch ($this->dbType) {
288
                    case 'sqlite':
289
                        $this->dbHandler = @new PDO("sqlite:" . $this->dbName, null, null, $this->pdoSettings);
290
                        break;
291
                    case 'mysql':
292
                    case 'pgsql':
293
                    case 'dblib':
294
                        $this->dbHandler = @new PDO(
295
                            $this->dsn,
296
                            $this->user,
297
                            $this->pass,
298
                            $this->pdoSettings
299
                        );
300
                        // Execute init commands once connected
301
                        foreach($this->dumpSettings['init_commands'] as $stmt) {
302
                            $this->dbHandler->exec($stmt);
303
                        }
304
                        // Store server version
305
                        $this->version = $this->dbHandler->getAttribute(PDO::ATTR_SERVER_VERSION);
306
                        break;
307
                    default:
308
                        throw new Exception("Unsupported database type (" . $this->dbType . ")");
309
                }
310
            }
311
        } catch (PDOException $e) {
312
            throw new Exception(
313
                "Connection to " . $this->dbType . " failed with message: " .
314
                $e->getMessage()
315
            );
316
        }
317
318
        if ( is_null($this->dbHandler) ) {
319
            throw new Exception("Connection to ". $this->dbType . "failed");
320
        }
321
322
        $this->dbHandler->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_NATURAL);
323
        $this->typeAdapter = TypeAdapterFactory::create($this->dbType, $this->dbHandler);
324
    }
325
326
    /**
327
     * Main call
328
     *
329
     * @param string $filename  Name of file to write sql dump to
330
     * @return null
331
     */
332
    public function start($filename = '')
333
    {
334
        // Output file can be redefined here
335
        if (!empty($filename)) {
336
            $this->fileName = $filename;
337
        }
338
339
        // Connect to database
340
        $this->connect();
341
342
        // Create output file
343
        $this->compressManager->open($this->fileName);
344
345
        // Write some basic info to output file
346
        $this->compressManager->write($this->getDumpFileHeader());
347
348
        // Store server settings and use sanner defaults to dump
349
        $this->compressManager->write(
350
            $this->typeAdapter->backup_parameters($this->dumpSettings)
351
        );
352
353
        if ($this->dumpSettings['databases']) {
354
            $this->compressManager->write(
355
                $this->typeAdapter->getDatabaseHeader($this->dbName)
356
            );
357
            if ($this->dumpSettings['add-drop-database']) {
358
                $this->compressManager->write(
359
                    $this->typeAdapter->add_drop_database($this->dbName)
360
                );
361
            }
362
        }
363
364
        // Get table, view and trigger structures from database
365
        $this->getDatabaseStructure();
366
367
        if ($this->dumpSettings['databases']) {
368
            $this->compressManager->write(
369
                $this->typeAdapter->databases($this->dbName)
370
            );
371
        }
372
373
        // If there still are some tables/views in include-tables array,
374
        // that means that some tables or views weren't found.
375
        // Give proper error and exit.
376
        // This check will be removed once include-tables supports regexps
377
        if (0 < count($this->dumpSettings['include-tables'])) {
378
            $name = implode(",", $this->dumpSettings['include-tables']);
379
            throw new Exception("Table (" . $name . ") not found in database");
380
        }
381
382
        $this->exportTables();
383
        $this->exportViews();
384
        $this->exportTriggers();
385
        $this->exportProcedures();
386
        $this->exportEvents();
387
388
        // Restore saved parameters
389
        $this->compressManager->write(
390
            $this->typeAdapter->restore_parameters($this->dumpSettings)
391
        );
392
        // Write some stats to output file
393
        $this->compressManager->write($this->getDumpFileFooter());
394
        // Close output file
395
        $this->compressManager->close();
396
    }
397
398
    /**
399
     * Returns header for dump file
400
     *
401
     * @return string
402
     */
403
    private function getDumpFileHeader()
404
    {
405
        $header = '';
406
        if ( !$this->dumpSettings['skip-comments'] ) {
407
            // Some info about software, source and time
408
            $header = "-- mysqldump-php https://github.com/ifsnop/mysqldump-php" . PHP_EOL .
409
                    "--" . PHP_EOL .
410
                    "-- Host: {$this->host}\tDatabase: {$this->dbName}" . PHP_EOL .
411
                    "-- ------------------------------------------------------" . PHP_EOL;
412
413
            if ( !empty($this->version) ) {
414
                $header .= "-- Server version \t" . $this->version . PHP_EOL;
415
            }
416
417
            if ( !$this->dumpSettings['skip-dump-date'] ) {
418
                $header .= "-- Date: " . date('r') . PHP_EOL . PHP_EOL;
419
            }
420
        }
421
        return $header;
422
    }
423
424
    /**
425
     * Returns footer for dump file
426
     *
427
     * @return string
428
     */
429
    private function getDumpFileFooter()
430
    {
431
        $footer = '';
432
        if (!$this->dumpSettings['skip-comments']) {
433
            $footer .= '-- Dump completed';
434
            if (!$this->dumpSettings['skip-dump-date']) {
435
                $footer .= ' on: ' . date('r');
436
            }
437
            $footer .= PHP_EOL;
438
        }
439
440
        return $footer;
441
    }
442
443
    /**
444
     * Reads table and views names from database.
445
     * Fills $this->tables array so they will be dumped later.
446
     *
447
     * @return null
448
     */
449
    private function getDatabaseStructure()
450
    {
451
        // Listing all tables from database
452 View Code Duplication
        if (empty($this->dumpSettings['include-tables'])) {
453
            // include all tables for now, blacklisting happens later
454
            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...
455
                array_push($this->tables, current($row));
456
            }
457
        } else {
458
            // include only the tables mentioned in include-tables
459
            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...
460
                if (in_array(current($row), $this->dumpSettings['include-tables'], true)) {
461
                    array_push($this->tables, current($row));
462
                    $elem = array_search(
463
                        current($row),
464
                        $this->dumpSettings['include-tables']
465
                    );
466
                    unset($this->dumpSettings['include-tables'][$elem]);
467
                }
468
            }
469
        }
470
471
        // Listing all views from database
472 View Code Duplication
        if (empty($this->dumpSettings['include-views'])) {
473
            // include all views for now, blacklisting happens later
474
            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...
475
                array_push($this->views, current($row));
476
            }
477
        } else {
478
            // include only the tables mentioned in include-tables
479
            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...
480
                if (in_array(current($row), $this->dumpSettings['include-views'], true)) {
481
                    array_push($this->views, current($row));
482
                    $elem = array_search(
483
                        current($row),
484
                        $this->dumpSettings['include-views']
485
                    );
486
                    unset($this->dumpSettings['include-views'][$elem]);
487
                }
488
            }
489
        }
490
491
        // Listing all triggers from database
492 View Code Duplication
        if (false === $this->dumpSettings['skip-triggers']) {
493
            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...
494
                array_push($this->triggers, $row['Trigger']);
495
            }
496
        }
497
498
        // Listing all procedures from database
499 View Code Duplication
        if ($this->dumpSettings['routines']) {
500
            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...
501
                array_push($this->procedures, $row['procedure_name']);
502
            }
503
        }
504
505
        // Listing all events from database
506 View Code Duplication
        if ($this->dumpSettings['events']) {
507
            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...
508
                array_push($this->events, $row['event_name']);
509
            }
510
        }
511
    }
512
513
    /**
514
     * Compare if $table name matches with a definition inside $arr
515
     * @param $table string
516
     * @param $arr array with strings or patterns
517
     * @return bool
518
     */
519
    private function matches($table, $arr) {
520
        $match = false;
521
522
        foreach ($arr as $pattern) {
523
            if ( '/' != $pattern[0] ) {
524
                continue;
525
            }
526
            if ( 1 == preg_match($pattern, $table) ) {
527
                $match = true;
528
            }
529
        }
530
531
        return in_array($table, $arr) || $match;
532
    }
533
534
    /**
535
     * Exports all the tables selected from database
536
     *
537
     * @return null
538
     */
539
    private function exportTables()
540
    {
541
        // Exporting tables one by one
542
        foreach ($this->tables as $table) {
543
            if ( $this->matches($table, $this->dumpSettings['exclude-tables']) ) {
544
                continue;
545
            }
546
            $this->getTableStructure($table);
547
            if ( false === $this->dumpSettings['no-data'] ) { // don't break compatibility with old trigger
548
                $this->listValues($table);
549
            } else if ( true === $this->dumpSettings['no-data']
550
                 || $this->matches($table, $this->dumpSettings['no-data']) ) {
551
                continue;
552
            } else {
553
                $this->listValues($table);
554
            }
555
        }
556
    }
557
558
    /**
559
     * Exports all the views found in database
560
     *
561
     * @return null
562
     */
563
    private function exportViews()
564
    {
565
        if (false === $this->dumpSettings['no-create-info']) {
566
            // Exporting views one by one
567
            foreach ($this->views as $view) {
568
                if ( $this->matches($view, $this->dumpSettings['exclude-tables']) ) {
569
                    continue;
570
                }
571
                $this->tableColumnTypes[$view] = $this->getTableColumnTypes($view);
572
                $this->getViewStructureTable($view);
573
            }
574
            foreach ($this->views as $view) {
575
                if ( $this->matches($view, $this->dumpSettings['exclude-tables']) ) {
576
                    continue;
577
                }
578
                $this->getViewStructureView($view);
579
            }
580
        }
581
    }
582
583
    /**
584
     * Exports all the triggers found in database
585
     *
586
     * @return null
587
     */
588
    private function exportTriggers()
589
    {
590
        // Exporting triggers one by one
591
        foreach ($this->triggers as $trigger) {
592
            $this->getTriggerStructure($trigger);
593
        }
594
    }
595
596
    /**
597
     * Exports all the procedures found in database
598
     *
599
     * @return null
600
     */
601
    private function exportProcedures()
602
    {
603
        // Exporting triggers one by one
604
        foreach ($this->procedures as $procedure) {
605
            $this->getProcedureStructure($procedure);
606
        }
607
    }
608
609
    /**
610
     * Exports all the events found in database
611
     *
612
     * @return null
613
     */
614
    private function exportEvents()
615
    {
616
        // Exporting triggers one by one
617
        foreach ($this->events as $event) {
618
            $this->getEventStructure($event);
619
        }
620
    }
621
622
    /**
623
     * Table structure extractor
624
     *
625
     * @todo move specific mysql code to typeAdapter
626
     * @param string $tableName  Name of table to export
627
     * @return null
628
     */
629
    private function getTableStructure($tableName)
630
    {
631
        if (!$this->dumpSettings['no-create-info']) {
632
            $ret = '';
633
            if (!$this->dumpSettings['skip-comments']) {
634
                $ret = "--" . PHP_EOL .
635
                    "-- Table structure for table `$tableName`" . PHP_EOL .
636
                    "--" . PHP_EOL . PHP_EOL;
637
            }
638
            $stmt = $this->typeAdapter->show_create_table($tableName);
639
            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...
640
                $this->compressManager->write($ret);
641
                if ($this->dumpSettings['add-drop-table']) {
642
                    $this->compressManager->write(
643
                        $this->typeAdapter->drop_table($tableName)
644
                    );
645
                }
646
                $this->compressManager->write(
647
                    $this->typeAdapter->create_table($r, $this->dumpSettings)
648
                );
649
                break;
650
            }
651
        }
652
        $this->tableColumnTypes[$tableName] = $this->getTableColumnTypes($tableName);
653
        return;
654
    }
655
656
    /**
657
     * Store column types to create data dumps and for Stand-In tables
658
     *
659
     * @param string $tableName  Name of table to export
660
     * @return array type column types detailed
661
     */
662
663
    private function getTableColumnTypes($tableName) {
664
        $columnTypes = array();
665
        $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...
666
            $this->typeAdapter->show_columns($tableName)
667
        );
668
        $columns->setFetchMode(PDO::FETCH_ASSOC);
669
670
        foreach($columns as $key => $col) {
671
            $types = $this->typeAdapter->parseColumnType($col);
672
            $columnTypes[$col['Field']] = array(
673
                'is_numeric'=> $types['is_numeric'],
674
                'is_blob' => $types['is_blob'],
675
                'type' => $types['type'],
676
                'type_sql' => $col['Type'],
677
                'is_virtual' => $types['is_virtual']
678
            );
679
        }
680
681
        return $columnTypes;
682
    }
683
684
    /**
685
     * View structure extractor, create table (avoids cyclic references)
686
     *
687
     * @todo move mysql specific code to typeAdapter
688
     * @param string $viewName  Name of view to export
689
     * @return null
690
     */
691 View Code Duplication
    private function getViewStructureTable($viewName)
692
    {
693
        if (!$this->dumpSettings['skip-comments']) {
694
            $ret = "--" . PHP_EOL .
695
                "-- Stand-In structure for view `${viewName}`" . PHP_EOL .
696
                "--" . PHP_EOL . PHP_EOL;
697
            $this->compressManager->write($ret);
698
        }
699
        $stmt = $this->typeAdapter->show_create_view($viewName);
700
701
        // create views as tables, to resolve dependencies
702
        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...
703
            if ($this->dumpSettings['add-drop-table']) {
704
                $this->compressManager->write(
705
                    $this->typeAdapter->drop_view($viewName)
706
                );
707
            }
708
709
            $this->compressManager->write(
710
                $this->createStandInTable($viewName)
711
            );
712
            break;
713
        }
714
    }
715
716
    /**
717
     * Write a create table statement for the table Stand-In, show create
718
     * table would return a create algorithm when used on a view
719
     *
720
     * @param string $viewName  Name of view to export
721
     * @return string create statement
722
     */
723
    function createStandInTable($viewName) {
724
        $ret = array();
725
        foreach($this->tableColumnTypes[$viewName] as $k => $v) {
726
            $ret[] = "`${k}` ${v['type_sql']}";
727
        }
728
        $ret = implode(PHP_EOL . ",", $ret);
729
730
        $ret = "CREATE TABLE IF NOT EXISTS `$viewName` (" .
731
            PHP_EOL . $ret . PHP_EOL . ");" . PHP_EOL;
732
733
        return $ret;
734
    }
735
736
    /**
737
     * View structure extractor, create view
738
     *
739
     * @todo move mysql specific code to typeAdapter
740
     * @param string $viewName  Name of view to export
741
     * @return null
742
     */
743 View Code Duplication
    private function getViewStructureView($viewName)
744
    {
745
        if (!$this->dumpSettings['skip-comments']) {
746
            $ret = "--" . PHP_EOL .
747
                "-- View structure for view `${viewName}`" . PHP_EOL .
748
                "--" . PHP_EOL . PHP_EOL;
749
            $this->compressManager->write($ret);
750
        }
751
        $stmt = $this->typeAdapter->show_create_view($viewName);
752
753
        // create views, to resolve dependencies
754
        // replacing tables with views
755
        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...
756
            // because we must replace table with view, we should delete it
757
            $this->compressManager->write(
758
                $this->typeAdapter->drop_view($viewName)
759
            );
760
            $this->compressManager->write(
761
                $this->typeAdapter->create_view($r)
762
            );
763
            break;
764
        }
765
    }
766
767
    /**
768
     * Trigger structure extractor
769
     *
770
     * @param string $triggerName  Name of trigger to export
771
     * @return null
772
     */
773
    private function getTriggerStructure($triggerName)
774
    {
775
        $stmt = $this->typeAdapter->show_create_trigger($triggerName);
776
        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...
777
            if ($this->dumpSettings['add-drop-trigger']) {
778
                $this->compressManager->write(
779
                    $this->typeAdapter->add_drop_trigger($triggerName)
780
                );
781
            }
782
            $this->compressManager->write(
783
                $this->typeAdapter->create_trigger($r)
784
            );
785
            return;
786
        }
787
    }
788
789
    /**
790
     * Procedure structure extractor
791
     *
792
     * @param string $procedureName  Name of procedure to export
793
     * @return null
794
     */
795 View Code Duplication
    private function getProcedureStructure($procedureName)
796
    {
797
        if (!$this->dumpSettings['skip-comments']) {
798
            $ret = "--" . PHP_EOL .
799
                "-- Dumping routines for database '" . $this->dbName . "'" . PHP_EOL .
800
                "--" . PHP_EOL . PHP_EOL;
801
            $this->compressManager->write($ret);
802
        }
803
        $stmt = $this->typeAdapter->show_create_procedure($procedureName);
804
        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...
805
            $this->compressManager->write(
806
                $this->typeAdapter->create_procedure($r, $this->dumpSettings)
807
            );
808
            return;
809
        }
810
    }
811
812
    /**
813
     * Event structure extractor
814
     *
815
     * @param string $eventName  Name of event to export
816
     * @return null
817
     */
818 View Code Duplication
    private function getEventStructure($eventName)
819
    {
820
        if (!$this->dumpSettings['skip-comments']) {
821
            $ret = "--" . PHP_EOL .
822
                "-- Dumping events for database '" . $this->dbName . "'" . PHP_EOL .
823
                "--" . PHP_EOL . PHP_EOL;
824
            $this->compressManager->write($ret);
825
        }
826
        $stmt = $this->typeAdapter->show_create_event($eventName);
827
        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...
828
            $this->compressManager->write(
829
                $this->typeAdapter->create_event($r, $this->dumpSettings)
830
            );
831
            return;
832
        }
833
    }
834
835
    /**
836
     * Escape values with quotes when needed
837
     *
838
     * @param string $tableName Name of table which contains rows
839
     * @param array $row Associative array of column names and values to be quoted
840
     *
841
     * @return string
842
     */
843
    private function escape($tableName, $row)
844
    {
845
        $ret = array();
846
        $columnTypes = $this->tableColumnTypes[$tableName];
847
        foreach ($row as $colName => $colValue) {
848
            if (is_null($colValue)) {
849
                $ret[] = "NULL";
850
            } elseif ($this->dumpSettings['hex-blob'] && $columnTypes[$colName]['is_blob']) {
851
                if ($columnTypes[$colName]['type'] == 'bit' || !empty($colValue)) {
852
                    $ret[] = "0x${colValue}";
853
                } else {
854
                    $ret[] = "''";
855
                }
856
            } elseif ($columnTypes[$colName]['is_numeric']) {
857
                $ret[] = $colValue;
858
            } else {
859
                $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...
860
            }
861
        }
862
        return $ret;
863
    }
864
865
    /**
866
     * Table rows extractor
867
     *
868
     * @param string $tableName  Name of table to export
869
     *
870
     * @return null
871
     */
872
    private function listValues($tableName)
873
    {
874
        $this->prepareListValues($tableName);
875
876
        $onlyOnce = true;
877
        $lineSize = 0;
878
879
        $colStmt = $this->getColumnStmt($tableName);
880
        $stmt = "SELECT " . implode(",", $colStmt) . " FROM `$tableName`";
881
882
        if ($this->dumpSettings['where']) {
883
            $stmt .= " WHERE {$this->dumpSettings['where']}";
884
        }
885
        $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...
886
        $resultSet->setFetchMode(PDO::FETCH_ASSOC);
887
888
        foreach ($resultSet as $row) {
889
            $vals = $this->escape($tableName, $row);
890
            if ($onlyOnce || !$this->dumpSettings['extended-insert']) {
891
892
                if ($this->dumpSettings['complete-insert']) {
893
                    $lineSize += $this->compressManager->write(
894
                        "INSERT INTO `$tableName` (" .
895
                        implode(", ", $colStmt) .
896
                        ") VALUES (" . implode(",", $vals) . ")"
897
                    );
898
                } else {
899
                    $lineSize += $this->compressManager->write(
900
                        "INSERT INTO `$tableName` VALUES (" . implode(",", $vals) . ")"
901
                    );
902
                }
903
                $onlyOnce = false;
904
            } else {
905
                $lineSize += $this->compressManager->write(",(" . implode(",", $vals) . ")");
906
            }
907
            if (($lineSize > $this->dumpSettings['net_buffer_length']) ||
908
                    !$this->dumpSettings['extended-insert']) {
909
                $onlyOnce = true;
910
                $lineSize = $this->compressManager->write(";" . PHP_EOL);
911
            }
912
        }
913
        $resultSet->closeCursor();
914
915
        if (!$onlyOnce) {
916
            $this->compressManager->write(";" . PHP_EOL);
917
        }
918
919
        $this->endListValues($tableName);
920
    }
921
922
    /**
923
     * Table rows extractor, append information prior to dump
924
     *
925
     * @param string $tableName  Name of table to export
926
     *
927
     * @return null
928
     */
929
    function prepareListValues($tableName)
930
    {
931
        if (!$this->dumpSettings['skip-comments']) {
932
            $this->compressManager->write(
933
                "--" . PHP_EOL .
934
                "-- Dumping data for table `$tableName`" .  PHP_EOL .
935
                "--" . PHP_EOL . PHP_EOL
936
            );
937
        }
938
939
        if ($this->dumpSettings['single-transaction']) {
940
            $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...
941
            $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...
942
        }
943
944
        if ($this->dumpSettings['lock-tables']) {
945
            $this->typeAdapter->lock_table($tableName);
946
        }
947
948
        if ($this->dumpSettings['add-locks']) {
949
            $this->compressManager->write(
950
                $this->typeAdapter->start_add_lock_table($tableName)
951
            );
952
        }
953
954
        if ($this->dumpSettings['disable-keys']) {
955
            $this->compressManager->write(
956
                $this->typeAdapter->start_add_disable_keys($tableName)
957
            );
958
        }
959
960
        // Disable autocommit for faster reload
961
        if ($this->dumpSettings['no-autocommit']) {
962
            $this->compressManager->write(
963
                $this->typeAdapter->start_disable_autocommit()
964
            );
965
        }
966
967
        return;
968
    }
969
970
    /**
971
     * Table rows extractor, close locks and commits after dump
972
     *
973
     * @param string $tableName  Name of table to export
974
     *
975
     * @return null
976
     */
977
    function endListValues($tableName)
978
    {
979
        if ($this->dumpSettings['disable-keys']) {
980
            $this->compressManager->write(
981
                $this->typeAdapter->end_add_disable_keys($tableName)
982
            );
983
        }
984
985
        if ($this->dumpSettings['add-locks']) {
986
            $this->compressManager->write(
987
                $this->typeAdapter->end_add_lock_table($tableName)
988
            );
989
        }
990
991
        if ($this->dumpSettings['single-transaction']) {
992
            $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...
993
        }
994
995
        if ($this->dumpSettings['lock-tables']) {
996
            $this->typeAdapter->unlock_table($tableName);
997
        }
998
999
        // Commit to enable autocommit
1000
        if ($this->dumpSettings['no-autocommit']) {
1001
            $this->compressManager->write(
1002
                $this->typeAdapter->end_disable_autocommit()
1003
            );
1004
        }
1005
1006
        $this->compressManager->write(PHP_EOL);
1007
1008
        return;
1009
    }
1010
1011
    /**
1012
     * Build SQL List of all columns on current table
1013
     *
1014
     * @param string $tableName  Name of table to get columns
1015
     *
1016
     * @return string SQL sentence with columns
1017
     */
1018
    function getColumnStmt($tableName)
1019
    {
1020
        $colStmt = array();
1021
        foreach($this->tableColumnTypes[$tableName] as $colName => $colType) {
1022
            if ($colType['type'] == 'bit' && $this->dumpSettings['hex-blob']) {
1023
                $colStmt[] = "LPAD(HEX(`${colName}`),2,'0') AS `${colName}`";
1024
            } else if ($colType['is_blob'] && $this->dumpSettings['hex-blob']) {
1025
                $colStmt[] = "HEX(`${colName}`) AS `${colName}`";
1026
            } else if ($colType['is_virtual']) {
1027
                $this->dumpSettings['complete-insert'] = true;
1028
                continue;
1029
            } else {
1030
                $colStmt[] = "`${colName}`";
1031
            }
1032
        }
1033
1034
        return $colStmt;
1035
    }
1036
}
1037
1038
/**
1039
 * Enum with all available compression methods
1040
 *
1041
 */
1042
abstract class CompressMethod
1043
{
1044
    public static $enums = array(
1045
        "None",
1046
        "Gzip",
1047
        "Bzip2"
1048
    );
1049
1050
    /**
1051
     * @param string $c
1052
     * @return boolean
1053
     */
1054
    public static function isValid($c)
1055
    {
1056
        return in_array($c, self::$enums);
1057
    }
1058
}
1059
1060
abstract class CompressManagerFactory
1061
{
1062
    /**
1063
     * @param string $c
1064
     * @return CompressBzip2|CompressGzip|CompressNone
1065
     */
1066 View Code Duplication
    public static function create($c)
1067
    {
1068
        $c = ucfirst(strtolower($c));
1069
        if (! CompressMethod::isValid($c)) {
1070
            throw new Exception("Compression method ($c) is not defined yet");
1071
        }
1072
1073
        $method =  __NAMESPACE__ . "\\" . "Compress" . $c;
1074
1075
        return new $method;
1076
    }
1077
}
1078
1079 View Code Duplication
class CompressBzip2 extends CompressManagerFactory
1080
{
1081
    private $fileHandler = null;
1082
1083
    public function __construct()
1084
    {
1085
        if (! function_exists("bzopen")) {
1086
            throw new Exception("Compression is enabled, but bzip2 lib is not installed or configured properly");
1087
        }
1088
    }
1089
1090
    /**
1091
     * @param string $filename
1092
     */
1093
    public function open($filename)
1094
    {
1095
        $this->fileHandler = bzopen($filename, "w");
1096
        if (false === $this->fileHandler) {
1097
            throw new Exception("Output file is not writable");
1098
        }
1099
1100
        return true;
1101
    }
1102
1103
    public function write($str)
1104
    {
1105
        if (false === ($bytesWritten = bzwrite($this->fileHandler, $str))) {
1106
            throw new Exception("Writting to file failed! Probably, there is no more free space left?");
1107
        }
1108
        return $bytesWritten;
1109
    }
1110
1111
    public function close()
1112
    {
1113
        return bzclose($this->fileHandler);
1114
    }
1115
}
1116
1117 View Code Duplication
class CompressGzip extends CompressManagerFactory
1118
{
1119
    private $fileHandler = null;
1120
1121
    public function __construct()
1122
    {
1123
        if (! function_exists("gzopen")) {
1124
            throw new Exception("Compression is enabled, but gzip lib is not installed or configured properly");
1125
        }
1126
    }
1127
1128
    /**
1129
     * @param string $filename
1130
     */
1131
    public function open($filename)
1132
    {
1133
        $this->fileHandler = gzopen($filename, "wb");
1134
        if (false === $this->fileHandler) {
1135
            throw new Exception("Output file is not writable");
1136
        }
1137
1138
        return true;
1139
    }
1140
1141
    public function write($str)
1142
    {
1143
        if (false === ($bytesWritten = gzwrite($this->fileHandler, $str))) {
1144
            throw new Exception("Writting to file failed! Probably, there is no more free space left?");
1145
        }
1146
        return $bytesWritten;
1147
    }
1148
1149
    public function close()
1150
    {
1151
        return gzclose($this->fileHandler);
1152
    }
1153
}
1154
1155
class CompressNone extends CompressManagerFactory
1156
{
1157
    private $fileHandler = null;
1158
1159
    /**
1160
     * @param string $filename
1161
     */
1162
    public function open($filename)
1163
    {
1164
        $this->fileHandler = fopen($filename, "wb");
1165
        if (false === $this->fileHandler) {
1166
            throw new Exception("Output file is not writable");
1167
        }
1168
1169
        return true;
1170
    }
1171
1172
    public function write($str)
1173
    {
1174
        if (false === ($bytesWritten = fwrite($this->fileHandler, $str))) {
1175
            throw new Exception("Writting to file failed! Probably, there is no more free space left?");
1176
        }
1177
        return $bytesWritten;
1178
    }
1179
1180
    public function close()
1181
    {
1182
        return fclose($this->fileHandler);
1183
    }
1184
}
1185
1186
/**
1187
 * Enum with all available TypeAdapter implementations
1188
 *
1189
 */
1190
abstract class TypeAdapter
1191
{
1192
    public static $enums = array(
1193
        "Sqlite",
1194
        "Mysql"
1195
    );
1196
1197
    /**
1198
     * @param string $c
1199
     * @return boolean
1200
     */
1201
    public static function isValid($c)
1202
    {
1203
        return in_array($c, self::$enums);
1204
    }
1205
}
1206
1207
/**
1208
 * TypeAdapter Factory
1209
 *
1210
 */
1211
abstract class TypeAdapterFactory
1212
{
1213
    /**
1214
     * @param string $c Type of database factory to create (Mysql, Sqlite,...)
1215
     * @param PDO $dbHandler
1216
     */
1217 View Code Duplication
    public static function create($c, $dbHandler = null)
1218
    {
1219
        $c = ucfirst(strtolower($c));
1220
        if (! TypeAdapter::isValid($c)) {
1221
            throw new Exception("Database type support for ($c) not yet available");
1222
        }
1223
        $method =  __NAMESPACE__ . "\\" . "TypeAdapter" . $c;
1224
        return new $method($dbHandler);
1225
    }
1226
1227
    /**
1228
     * function databases Add sql to create and use database
1229
     * @todo make it do something with sqlite
1230
     */
1231
    public function databases()
1232
    {
1233
        return "";
1234
    }
1235
1236
    public function show_create_table($tableName)
1237
    {
1238
        return "SELECT tbl_name as 'Table', sql as 'Create Table' " .
1239
            "FROM sqlite_master " .
1240
            "WHERE type='table' AND tbl_name='$tableName'";
1241
    }
1242
1243
    /**
1244
     * function create_table Get table creation code from database
1245
     * @todo make it do something with sqlite
1246
     */
1247
    public function create_table($row, $dumpSettings)
1248
    {
1249
        return "";
1250
    }
1251
1252
    public function show_create_view($viewName)
1253
    {
1254
        return "SELECT tbl_name as 'View', sql as 'Create View' " .
1255
            "FROM sqlite_master " .
1256
            "WHERE type='view' AND tbl_name='$viewName'";
1257
    }
1258
1259
    /**
1260
     * function create_view Get view creation code from database
1261
     * @todo make it do something with sqlite
1262
     */
1263
    public function create_view($row)
1264
    {
1265
        return "";
1266
    }
1267
1268
    /**
1269
     * function show_create_trigger Get trigger creation code from database
1270
     * @todo make it do something with sqlite
1271
     */
1272
    public function show_create_trigger($triggerName)
1273
    {
1274
        return "";
1275
    }
1276
1277
    /**
1278
     * function create_trigger Modify trigger code, add delimiters, etc
1279
     * @todo make it do something with sqlite
1280
     */
1281
    public function create_trigger($triggerName)
1282
    {
1283
        return "";
1284
    }
1285
1286
    /**
1287
     * function create_procedure Modify procedure code, add delimiters, etc
1288
     * @todo make it do something with sqlite
1289
     */
1290
    public function create_procedure($procedureName, $dumpSettings)
1291
    {
1292
        return "";
1293
    }
1294
1295
    public function show_tables()
1296
    {
1297
        return "SELECT tbl_name FROM sqlite_master WHERE type='table'";
1298
    }
1299
1300
    public function show_views()
1301
    {
1302
        return "SELECT tbl_name FROM sqlite_master WHERE type='view'";
1303
    }
1304
1305
    public function show_triggers()
1306
    {
1307
        return "SELECT name FROM sqlite_master WHERE type='trigger'";
1308
    }
1309
1310
    public function show_columns()
1311
    {
1312
        if (func_num_args() != 1) {
1313
            return "";
1314
        }
1315
1316
        $args = func_get_args();
1317
1318
        return "pragma table_info(${args[0]})";
1319
    }
1320
1321
    public function show_procedures()
1322
    {
1323
        return "";
1324
    }
1325
1326
    public function show_events()
1327
    {
1328
        return "";
1329
    }
1330
1331
    public function setup_transaction()
1332
    {
1333
        return "";
1334
    }
1335
1336
    public function start_transaction()
1337
    {
1338
        return "BEGIN EXCLUSIVE";
1339
    }
1340
1341
    public function commit_transaction()
1342
    {
1343
        return "COMMIT";
1344
    }
1345
1346
    public function lock_table()
1347
    {
1348
        return "";
1349
    }
1350
1351
    public function unlock_table()
1352
    {
1353
        return "";
1354
    }
1355
1356
    public function start_add_lock_table()
1357
    {
1358
        return PHP_EOL;
1359
    }
1360
1361
    public function end_add_lock_table()
1362
    {
1363
        return PHP_EOL;
1364
    }
1365
1366
    public function start_add_disable_keys()
1367
    {
1368
        return PHP_EOL;
1369
    }
1370
1371
    public function end_add_disable_keys()
1372
    {
1373
        return PHP_EOL;
1374
    }
1375
1376
    public function start_disable_foreign_keys_check()
1377
    {
1378
        return PHP_EOL;
1379
    }
1380
1381
    public function end_disable_foreign_keys_check()
1382
    {
1383
        return PHP_EOL;
1384
    }
1385
1386
    public function add_drop_database()
1387
    {
1388
        return PHP_EOL;
1389
    }
1390
1391
    public function add_drop_trigger()
1392
    {
1393
        return PHP_EOL;
1394
    }
1395
1396
    public function drop_table()
1397
    {
1398
        return PHP_EOL;
1399
    }
1400
1401
    public function drop_view()
1402
    {
1403
        return PHP_EOL;
1404
    }
1405
1406
    /**
1407
     * Decode column metadata and fill info structure.
1408
     * type, is_numeric and is_blob will always be available.
1409
     *
1410
     * @param array $colType Array returned from "SHOW COLUMNS FROM tableName"
1411
     * @return array
1412
     */
1413
    public function parseColumnType($colType)
1414
    {
1415
        return array();
1416
    }
1417
1418
    public function backup_parameters()
1419
    {
1420
        return PHP_EOL;
1421
    }
1422
1423
    public function restore_parameters()
1424
    {
1425
        return PHP_EOL;
1426
    }
1427
}
1428
1429
class TypeAdapterPgsql extends TypeAdapterFactory
1430
{
1431
}
1432
1433
class TypeAdapterDblib extends TypeAdapterFactory
1434
{
1435
}
1436
1437
class TypeAdapterSqlite extends TypeAdapterFactory
1438
{
1439
}
1440
1441
class TypeAdapterMysql extends TypeAdapterFactory
1442
{
1443
1444
    private $dbHandler = null;
1445
1446
    // Numerical Mysql types
1447
    public $mysqlTypes = array(
1448
        'numerical' => array(
1449
            'bit',
1450
            'tinyint',
1451
            'smallint',
1452
            'mediumint',
1453
            'int',
1454
            'integer',
1455
            'bigint',
1456
            'real',
1457
            'double',
1458
            'float',
1459
            'decimal',
1460
            'numeric'
1461
        ),
1462
        'blob' => array(
1463
            'tinyblob',
1464
            'blob',
1465
            'mediumblob',
1466
            'longblob',
1467
            'binary',
1468
            'varbinary',
1469
            'bit',
1470
            'geometry', /* http://bugs.mysql.com/bug.php?id=43544 */
1471
            'point',
1472
            'linestring',
1473
            'polygon',
1474
            'multipoint',
1475
            'multilinestring',
1476
            'multipolygon',
1477
            'geometrycollection',
1478
        )
1479
    );
1480
1481
    public function __construct ($dbHandler)
1482
    {
1483
        $this->dbHandler = $dbHandler;
1484
    }
1485
1486
    public function databases()
1487
    {
1488
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1489
        $args = func_get_args();
1490
        $databaseName = $args[0];
1491
1492
        $resultSet = $this->dbHandler->query("SHOW VARIABLES LIKE 'character_set_database';");
1493
        $characterSet = $resultSet->fetchColumn(1);
1494
        $resultSet->closeCursor();
1495
1496
        $resultSet = $this->dbHandler->query("SHOW VARIABLES LIKE 'collation_database';");
1497
        $collationDb = $resultSet->fetchColumn(1);
1498
        $resultSet->closeCursor();
1499
        $ret = "";
1500
1501
        $ret .= "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `${databaseName}`".
1502
            " /*!40100 DEFAULT CHARACTER SET ${characterSet} " .
1503
            " COLLATE ${collationDb} */;" . PHP_EOL . PHP_EOL .
1504
            "USE `${databaseName}`;" . PHP_EOL . PHP_EOL;
1505
1506
        return $ret;
1507
    }
1508
1509
    public function show_create_table($tableName)
1510
    {
1511
        return "SHOW CREATE TABLE `$tableName`";
1512
    }
1513
1514
    public function show_create_view($viewName)
1515
    {
1516
        return "SHOW CREATE VIEW `$viewName`";
1517
    }
1518
1519
    public function show_create_trigger($triggerName)
1520
    {
1521
        return "SHOW CREATE TRIGGER `$triggerName`";
1522
    }
1523
1524
    public function show_create_procedure($procedureName)
1525
    {
1526
        return "SHOW CREATE PROCEDURE `$procedureName`";
1527
    }
1528
1529
    public function show_create_event($eventName)
1530
    {
1531
        return "SHOW CREATE EVENT `$eventName`";
1532
    }
1533
1534
    public function create_table( $row, $dumpSettings )
1535
    {
1536
        if ( !isset($row['Create Table']) ) {
1537
            throw new Exception("Error getting table code, unknown output");
1538
        }
1539
1540
        $createTable = $row['Create Table'];
1541
        if ( $dumpSettings['reset-auto-increment'] ) {
1542
            $match = "/AUTO_INCREMENT=[0-9]+/s";
1543
            $replace = "";
1544
            $createTable = preg_replace($match, $replace, $createTable);
1545
        }
1546
1547
        $ret = "/*!40101 SET @saved_cs_client     = @@character_set_client */;" . PHP_EOL .
1548
            "/*!40101 SET character_set_client = " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL .
1549
            $createTable . ";" . PHP_EOL .
1550
            "/*!40101 SET character_set_client = @saved_cs_client */;" . PHP_EOL .
1551
            PHP_EOL;
1552
        return $ret;
1553
    }
1554
1555
    public function create_view($row)
1556
    {
1557
        $ret = "";
1558
        if (!isset($row['Create View'])) {
1559
                throw new Exception("Error getting view structure, unknown output");
1560
        }
1561
1562
        $triggerStmt = $row['Create View'];
1563
1564
        $triggerStmtReplaced1 = str_replace(
1565
            "CREATE ALGORITHM",
1566
            "/*!50001 CREATE ALGORITHM",
1567
            $triggerStmt
1568
        );
1569
        $triggerStmtReplaced2 = str_replace(
1570
            " DEFINER=",
1571
            " */" . PHP_EOL . "/*!50013 DEFINER=",
1572
            $triggerStmtReplaced1
1573
        );
1574
        $triggerStmtReplaced3 = str_replace(
1575
            " VIEW ",
1576
            " */" . PHP_EOL . "/*!50001 VIEW ",
1577
            $triggerStmtReplaced2
1578
        );
1579
        if (false === $triggerStmtReplaced1 ||
1580
            false === $triggerStmtReplaced2 ||
1581
            false === $triggerStmtReplaced3) {
1582
            $triggerStmtReplaced = $triggerStmt;
1583
        } else {
1584
            $triggerStmtReplaced = $triggerStmtReplaced3 . " */;";
1585
        }
1586
1587
        $ret .= $triggerStmtReplaced . PHP_EOL . PHP_EOL;
1588
        return $ret;
1589
    }
1590
1591
    public function create_trigger($row)
1592
    {
1593
        $ret = "";
1594
        if (!isset($row['SQL Original Statement'])) {
1595
            throw new Exception("Error getting trigger code, unknown output");
1596
        }
1597
1598
        $triggerStmt = $row['SQL Original Statement'];
1599
        $triggerStmtReplaced = str_replace(
1600
            "CREATE DEFINER",
1601
            "/*!50003 CREATE*/ /*!50017 DEFINER",
1602
            $triggerStmt
1603
        );
1604
        $triggerStmtReplaced = str_replace(
1605
            " TRIGGER",
1606
            "*/ /*!50003 TRIGGER",
1607
            $triggerStmtReplaced
1608
        );
1609
        if ( false === $triggerStmtReplaced ) {
1610
            $triggerStmtReplaced = $triggerStmt . " /* ";
1611
        }
1612
1613
        $ret .= "DELIMITER ;;" . PHP_EOL .
1614
            $triggerStmtReplaced . " */ ;;" . PHP_EOL .
1615
            "DELIMITER ;" . PHP_EOL . PHP_EOL;
1616
        return $ret;
1617
    }
1618
1619
    public function create_procedure($row, $dumpSettings)
1620
    {
1621
        $ret = "";
1622
        if (!isset($row['Create Procedure'])) {
1623
            throw new Exception("Error getting procedure code, unknown output. " .
1624
                "Please check 'https://bugs.mysql.com/bug.php?id=14564'");
1625
        }
1626
        $procedureStmt = $row['Create Procedure'];
1627
1628
        $ret .= "/*!50003 DROP PROCEDURE IF EXISTS `" .
1629
            $row['Procedure'] . "` */;" . PHP_EOL .
1630
            "/*!40101 SET @saved_cs_client     = @@character_set_client */;" . PHP_EOL .
1631
            "/*!40101 SET character_set_client = " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL .
1632
            "DELIMITER ;;" . PHP_EOL .
1633
            $procedureStmt . " ;;" . PHP_EOL .
1634
            "DELIMITER ;" . PHP_EOL .
1635
            "/*!40101 SET character_set_client = @saved_cs_client */;" . PHP_EOL . PHP_EOL;
1636
1637
        return $ret;
1638
    }
1639
1640
    public function create_event($row)
1641
    {
1642
        $ret = "";
1643
        if ( !isset($row['Create Event']) ) {
1644
            throw new Exception("Error getting event code, unknown output. " .
1645
                "Please check 'http://stackoverflow.com/questions/10853826/mysql-5-5-create-event-gives-syntax-error'");
1646
        }
1647
        $eventName = $row['Event'];
1648
        $eventStmt = $row['Create Event'];
1649
        $sqlMode = $row['sql_mode'];
1650
1651
        $eventStmtReplaced = str_replace(
1652
            "CREATE DEFINER",
1653
            "/*!50106 CREATE*/ /*!50117 DEFINER",
1654
            $eventStmt
1655
        );
1656
        $eventStmtReplaced = str_replace(
1657
            " EVENT ",
1658
            "*/ /*!50106 EVENT ",
1659
            $eventStmtReplaced
1660
        );
1661
1662
        if ( false === $eventStmtReplaced ) {
1663
            $eventStmtReplaced = $eventStmt . " /* ";
1664
        }
1665
1666
        $ret .= "/*!50106 SET @save_time_zone= @@TIME_ZONE */ ;" . PHP_EOL .
1667
            "/*!50106 DROP EVENT IF EXISTS `" . $eventName . "` */;" . PHP_EOL .
1668
            "DELIMITER ;;" . PHP_EOL .
1669
            "/*!50003 SET @saved_cs_client      = @@character_set_client */ ;;" . PHP_EOL .
1670
            "/*!50003 SET @saved_cs_results     = @@character_set_results */ ;;" . PHP_EOL .
1671
            "/*!50003 SET @saved_col_connection = @@collation_connection */ ;;" . PHP_EOL .
1672
            "/*!50003 SET character_set_client  = utf8 */ ;;" . PHP_EOL .
1673
            "/*!50003 SET character_set_results = utf8 */ ;;" . PHP_EOL .
1674
            "/*!50003 SET collation_connection  = utf8_general_ci */ ;;" . PHP_EOL .
1675
            "/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;;" . PHP_EOL .
1676
            "/*!50003 SET sql_mode              = '" . $sqlMode . "' */ ;;" . PHP_EOL .
1677
            "/*!50003 SET @saved_time_zone      = @@time_zone */ ;;" . PHP_EOL .
1678
            "/*!50003 SET time_zone             = 'SYSTEM' */ ;;" . PHP_EOL .
1679
            $eventStmtReplaced . " */ ;;" . PHP_EOL .
1680
            "/*!50003 SET time_zone             = @saved_time_zone */ ;;" . PHP_EOL .
1681
            "/*!50003 SET sql_mode              = @saved_sql_mode */ ;;" . PHP_EOL .
1682
            "/*!50003 SET character_set_client  = @saved_cs_client */ ;;" . PHP_EOL .
1683
            "/*!50003 SET character_set_results = @saved_cs_results */ ;;" . PHP_EOL .
1684
            "/*!50003 SET collation_connection  = @saved_col_connection */ ;;" . PHP_EOL .
1685
            "DELIMITER ;" . PHP_EOL .
1686
            "/*!50106 SET TIME_ZONE= @save_time_zone */ ;" . PHP_EOL . PHP_EOL;
1687
            // Commented because we are doing this in restore_parameters()
1688
            // "/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;" . PHP_EOL . PHP_EOL;
1689
1690
        return $ret;
1691
    }
1692
1693 View Code Duplication
    public function show_tables()
1694
    {
1695
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1696
        $args = func_get_args();
1697
        return "SELECT TABLE_NAME AS tbl_name " .
1698
            "FROM INFORMATION_SCHEMA.TABLES " .
1699
            "WHERE TABLE_TYPE='BASE TABLE' AND TABLE_SCHEMA='${args[0]}'";
1700
    }
1701
1702 View Code Duplication
    public function show_views()
1703
    {
1704
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1705
        $args = func_get_args();
1706
        return "SELECT TABLE_NAME AS tbl_name " .
1707
            "FROM INFORMATION_SCHEMA.TABLES " .
1708
            "WHERE TABLE_TYPE='VIEW' AND TABLE_SCHEMA='${args[0]}'";
1709
    }
1710
1711
    public function show_triggers()
1712
    {
1713
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1714
        $args = func_get_args();
1715
        return "SHOW TRIGGERS FROM `${args[0]}`;";
1716
    }
1717
1718
    public function show_columns()
1719
    {
1720
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1721
        $args = func_get_args();
1722
        return "SHOW COLUMNS FROM `${args[0]}`;";
1723
    }
1724
1725 View Code Duplication
    public function show_procedures()
1726
    {
1727
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1728
        $args = func_get_args();
1729
        return "SELECT SPECIFIC_NAME AS procedure_name " .
1730
            "FROM INFORMATION_SCHEMA.ROUTINES " .
1731
            "WHERE ROUTINE_TYPE='PROCEDURE' AND ROUTINE_SCHEMA='${args[0]}'";
1732
    }
1733
1734
    /**
1735
     * Get query string to ask for names of events from current database.
1736
     *
1737
     * @param string Name of database
1738
     * @return string
1739
     */
1740 View Code Duplication
    public function show_events()
1741
    {
1742
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1743
        $args = func_get_args();
1744
        return "SELECT EVENT_NAME AS event_name " .
1745
            "FROM INFORMATION_SCHEMA.EVENTS " .
1746
            "WHERE EVENT_SCHEMA='${args[0]}'";
1747
    }
1748
1749
    public function setup_transaction()
1750
    {
1751
        return "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ";
1752
    }
1753
1754
    public function start_transaction()
1755
    {
1756
        return "START TRANSACTION";
1757
    }
1758
1759
    public function commit_transaction()
1760
    {
1761
        return "COMMIT";
1762
    }
1763
1764
    public function lock_table()
1765
    {
1766
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1767
        $args = func_get_args();
1768
        return $this->dbHandler->exec("LOCK TABLES `${args[0]}` READ LOCAL");
1769
1770
    }
1771
1772
    public function unlock_table()
1773
    {
1774
        return $this->dbHandler->exec("UNLOCK TABLES");
1775
    }
1776
1777
    public function start_add_lock_table()
1778
    {
1779
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1780
        $args = func_get_args();
1781
        return "LOCK TABLES `${args[0]}` WRITE;" . PHP_EOL;
1782
    }
1783
1784
    public function end_add_lock_table()
1785
    {
1786
        return "UNLOCK TABLES;" . PHP_EOL;
1787
    }
1788
1789 View Code Duplication
    public function start_add_disable_keys()
1790
    {
1791
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1792
        $args = func_get_args();
1793
        return "/*!40000 ALTER TABLE `${args[0]}` DISABLE KEYS */;" .
1794
            PHP_EOL;
1795
    }
1796
1797 View Code Duplication
    public function end_add_disable_keys()
1798
    {
1799
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1800
        $args = func_get_args();
1801
        return "/*!40000 ALTER TABLE `${args[0]}` ENABLE KEYS */;" .
1802
            PHP_EOL;
1803
    }
1804
1805
    public function start_disable_autocommit()
1806
    {
1807
        return "SET autocommit=0;" . PHP_EOL;
1808
    }
1809
1810
    public function end_disable_autocommit()
1811
    {
1812
        return "COMMIT;" . PHP_EOL;
1813
    }
1814
1815 View Code Duplication
    public function add_drop_database()
1816
    {
1817
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1818
        $args = func_get_args();
1819
        return "/*!40000 DROP DATABASE IF EXISTS `${args[0]}`*/;" .
1820
            PHP_EOL . PHP_EOL;
1821
    }
1822
1823
    public function add_drop_trigger()
1824
    {
1825
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1826
        $args = func_get_args();
1827
        return "DROP TRIGGER IF EXISTS `${args[0]}`;" . PHP_EOL;
1828
    }
1829
1830
    public function drop_table()
1831
    {
1832
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1833
        $args = func_get_args();
1834
        return "DROP TABLE IF EXISTS `${args[0]}`;" . PHP_EOL;
1835
    }
1836
1837 View Code Duplication
    public function drop_view()
1838
    {
1839
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1840
        $args = func_get_args();
1841
        return "DROP TABLE IF EXISTS `${args[0]}`;" . PHP_EOL .
1842
                "/*!50001 DROP VIEW IF EXISTS `${args[0]}`*/;" . PHP_EOL;
1843
    }
1844
1845
    public function getDatabaseHeader()
1846
    {
1847
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1848
        $args = func_get_args();
1849
        return "--" . PHP_EOL .
1850
            "-- Current Database: `${args[0]}`" . PHP_EOL .
1851
            "--" . PHP_EOL . PHP_EOL;
1852
    }
1853
1854
    /**
1855
     * Decode column metadata and fill info structure.
1856
     * type, is_numeric and is_blob will always be available.
1857
     *
1858
     * @param array $colType Array returned from "SHOW COLUMNS FROM tableName"
1859
     * @return array
1860
     */
1861
    public function parseColumnType($colType)
1862
    {
1863
        $colInfo = array();
1864
        $colParts = explode(" ", $colType['Type']);
1865
1866
        if($fparen = strpos($colParts[0], "("))
1867
        {
1868
            $colInfo['type'] = substr($colParts[0], 0, $fparen);
1869
            $colInfo['length']  = str_replace(")", "", substr($colParts[0], $fparen+1));
1870
            $colInfo['attributes'] = isset($colParts[1]) ? $colParts[1] : NULL;
1871
        }
1872
        else
1873
        {
1874
            $colInfo['type'] = $colParts[0];
1875
        }
1876
        $colInfo['is_numeric'] = in_array($colInfo['type'], $this->mysqlTypes['numerical']);
1877
        $colInfo['is_blob'] = in_array($colInfo['type'], $this->mysqlTypes['blob']);
1878
        // for virtual 'Extra' -> "STORED GENERATED"
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1879
        $colInfo['is_virtual'] = strpos($colType['Extra'], "STORED GENERATED") === false ? false : true;
1880
1881
        return $colInfo;
1882
    }
1883
1884
    public function backup_parameters()
1885
    {
1886
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1887
        $args = func_get_args();
1888
        $dumpSettings = $args[0];
1889
        $ret = "/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;" . PHP_EOL .
1890
            "/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;" . PHP_EOL .
1891
            "/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;" . PHP_EOL .
1892
            "/*!40101 SET NAMES " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL;
1893
1894
        if (false === $dumpSettings['skip-tz-utc']) {
1895
            $ret .= "/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;" . PHP_EOL .
1896
                "/*!40103 SET TIME_ZONE='+00:00' */;" . PHP_EOL;
1897
        }
1898
1899
        $ret .= "/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;" . PHP_EOL .
1900
            "/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;" . PHP_EOL .
1901
            "/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;" . PHP_EOL .
1902
            "/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;" . PHP_EOL .PHP_EOL;
1903
1904
        return $ret;
1905
    }
1906
1907
    public function restore_parameters()
1908
    {
1909
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1910
        $args = func_get_args();
1911
        $dumpSettings = $args[0];
1912
        $ret = "";
1913
1914
        if (false === $dumpSettings['skip-tz-utc']) {
1915
            $ret .= "/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;" . PHP_EOL;
1916
        }
1917
1918
        $ret .= "/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;" . PHP_EOL .
1919
            "/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;" . PHP_EOL .
1920
            "/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;" . PHP_EOL .
1921
            "/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;" . PHP_EOL .
1922
            "/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;" . PHP_EOL .
1923
            "/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;" . PHP_EOL .
1924
            "/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;" . PHP_EOL . PHP_EOL;
1925
1926
        return $ret;
1927
    }
1928
1929
    /**
1930
     * Check number of parameters passed to function, useful when inheriting.
1931
     * Raise exception if unexpected.
1932
     *
1933
     * @param integer $num_args
1934
     * @param integer $expected_num_args
1935
     * @param string $method_name
1936
     */
1937
    private function check_parameters($num_args, $expected_num_args, $method_name)
1938
    {
1939
        if ( $num_args != $expected_num_args ) {
1940
            throw new Exception("Unexpected parameter passed to $method_name");
1941
        }
1942
        return;
1943
    }
1944
}
1945