· 9 years ago · Apr 07, 2017, 01:34 AM
1<?php
2
3namespace Ifsnop\Mysqldump;
4
5use Exception;
6use PDO;
7use PDOException;
8
9
10class Mysqldump
11{
12
13 const MAXLINESIZE = 1000000;
14
15
16 const GZIP = 'Gzip';
17 const BZIP2 = 'Bzip2';
18 const NONE = 'None';
19
20
21 const UTF8 = 'utf8';
22 const UTF8MB4 = 'utf8mb4';
23
24
25 public $user;
26
27 public $pass;
28
29 public $dsn;
30
31 public $fileName = 'php://output';
32
33
34 private $tables = array();
35 private $views = array();
36 private $triggers = array();
37 private $procedures = array();
38 private $dbHandler = null;
39 private $dbType;
40 private $compressManager;
41 private $typeAdapter;
42 private $dumpSettings = array();
43 private $pdoSettings = array();
44 private $version;
45 private $tableColumnTypes = array();
46
47 private $dbName;
48
49 private $host;
50
51 private $dsnArray = array();
52
53 public function __construct(
54 $dsn = '',
55 $user = '',
56 $pass = '',
57 $dumpSettings = array(),
58 $pdoSettings = array()
59 ) {
60 $dumpSettingsDefault = array(
61 'include-tables' => array(),
62 'exclude-tables' => array(),
63 'compress' => Mysqldump::NONE,
64 'no-data' => array(),
65 'reset-auto-increment' => false,
66 'add-drop-table' => false,
67 'single-transaction' => true,
68 'lock-tables' => true,
69 'add-locks' => true,
70 'extended-insert' => true,
71 'complete-insert' => false,
72 'disable-keys' => true,
73 'where' => '',
74 'no-create-info' => false,
75 'skip-triggers' => false,
76 'add-drop-trigger' => true,
77 'routines' => false,
78 'hex-blob' => true, /* faster than escaped content */
79 'databases' => false,
80 'add-drop-database' => false,
81 'skip-tz-utc' => false,
82 'no-autocommit' => true,
83 'default-character-set' => Mysqldump::UTF8,
84 'skip-comments' => false,
85 'skip-dump-date' => false,
86 'init_commands' => array(),
87 'net_buffer_length' => self::MAXLINESIZE,
88 /* deprecated */
89 'disable-foreign-keys-check' => true
90 );
91
92 $pdoSettingsDefault = array(
93 PDO::ATTR_PERSISTENT => true,
94 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
95 PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false
96 );
97
98 $this->user = $user;
99 $this->pass = $pass;
100 $this->parseDsn($dsn);
101 $this->pdoSettings = self::array_replace_recursive($pdoSettingsDefault, $pdoSettings);
102 $this->dumpSettings = self::array_replace_recursive($dumpSettingsDefault, $dumpSettings);
103
104 $this->dumpSettings['init_commands'][] = "SET NAMES " . $this->dumpSettings['default-character-set'];
105
106 if (false === $this->dumpSettings['skip-tz-utc']) {
107 $this->dumpSettings['init_commands'][] = "SET TIME_ZONE='+00:00'";
108 }
109
110 $diff = array_diff(array_keys($this->dumpSettings), array_keys($dumpSettingsDefault));
111 if (count($diff)>0) {
112 throw new Exception("Unexpected value in dumpSettings: (" . implode(",", $diff) . ")");
113 }
114
115 if ( !is_array($this->dumpSettings['include-tables']) ||
116 !is_array($this->dumpSettings['exclude-tables']) ) {
117 throw new Exception("Include-tables and exclude-tables should be arrays");
118 }
119
120 // Dump the same views as tables, mimic mysqldump behaviour
121 $this->dumpSettings['include-views'] = $this->dumpSettings['include-tables'];
122
123 // Create a new compressManager to manage compressed output
124 $this->compressManager = CompressManagerFactory::create($this->dumpSettings['compress']);
125 }
126
127 /**
128 * Destructor of Mysqldump. Unsets dbHandlers and database objects.
129 *
130 */
131 public function __destruct()
132 {
133 $this->dbHandler = null;
134 }
135
136
137 public static function array_replace_recursive($array1, $array2)
138 {
139 if (function_exists('array_replace_recursive')) {
140 return array_replace_recursive($array1, $array2);
141 }
142
143 foreach ($array2 as $key => $value) {
144 if (is_array($value)) {
145 $array1[$key] = self::array_replace_recursive($array1[$key], $value);
146 } else {
147 $array1[$key] = $value;
148 }
149 }
150 return $array1;
151 }
152
153
154 private function parseDsn($dsn)
155 {
156 if (empty($dsn) || (false === ($pos = strpos($dsn, ":")))) {
157 throw new Exception("Empty DSN string");
158 }
159
160 $this->dsn = $dsn;
161 $this->dbType = strtolower(substr($dsn, 0, $pos));
162
163 if (empty($this->dbType)) {
164 throw new Exception("Missing database type from DSN string");
165 }
166
167 $dsn = substr($dsn, $pos + 1);
168
169 foreach(explode(";", $dsn) as $kvp) {
170 $kvpArr = explode("=", $kvp);
171 $this->dsnArray[strtolower($kvpArr[0])] = $kvpArr[1];
172 }
173
174 if (empty($this->dsnArray['host']) &&
175 empty($this->dsnArray['unix_socket'])) {
176 throw new Exception("Missing host from DSN string");
177 }
178 $this->host = (!empty($this->dsnArray['host'])) ?
179 $this->dsnArray['host'] :
180 $this->dsnArray['unix_socket'];
181
182 if (empty($this->dsnArray['dbname'])) {
183 throw new Exception("Missing database name from DSN string");
184 }
185
186 $this->dbName = $this->dsnArray['dbname'];
187
188 return true;
189 }
190
191
192 private function connect()
193 {
194
195 try {
196 switch ($this->dbType) {
197 case 'sqlite':
198 $this->dbHandler = @new PDO("sqlite:" . $this->dbName, null, null, $this->pdoSettings);
199 break;
200 case 'mysql':
201 case 'pgsql':
202 case 'dblib':
203 $this->dbHandler = @new PDO(
204 $this->dsn,
205 $this->user,
206 $this->pass,
207 $this->pdoSettings
208 );
209 foreach($this->dumpSettings['init_commands'] as $stmt) {
210 $this->dbHandler->exec($stmt);
211 }
212 $this->version = $this->dbHandler->getAttribute(PDO::ATTR_SERVER_VERSION);
213 break;
214 default:
215 throw new Exception("Unsupported database type (" . $this->dbType . ")");
216 }
217 } catch (PDOException $e) {
218 throw new Exception(
219 "Connection to " . $this->dbType . " failed with message: " .
220 $e->getMessage()
221 );
222 }
223
224 if ( is_null($this->dbHandler) ) {
225 throw new Exception("Connection to ". $this->dbType . "failed");
226 }
227
228 $this->dbHandler->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_NATURAL);
229 $this->typeAdapter = TypeAdapterFactory::create($this->dbType, $this->dbHandler);
230 }
231
232 /**
233 * Main call
234 *
235 * @param string $filename Name of file to write sql dump to
236 * @return null
237 */
238 public function start($filename = '')
239 {
240 // Output file can be redefined here
241 if (!empty($filename)) {
242 $this->fileName = $filename;
243 }
244
245 // Connect to database
246 $this->connect();
247
248 // Create output file
249 $this->compressManager->open($this->fileName);
250
251 // Write some basic info to output file
252 $this->compressManager->write($this->getDumpFileHeader());
253
254 // Store server settings and use sanner defaults to dump
255 $this->compressManager->write(
256 $this->typeAdapter->backup_parameters($this->dumpSettings)
257 );
258
259 if ($this->dumpSettings['databases']) {
260 $this->compressManager->write(
261 $this->typeAdapter->getDatabaseHeader($this->dbName)
262 );
263 if ($this->dumpSettings['add-drop-database']) {
264 $this->compressManager->write(
265 $this->typeAdapter->add_drop_database($this->dbName)
266 );
267 }
268 }
269
270 // Get table, view and trigger structures from database
271 $this->getDatabaseStructure();
272
273 if ($this->dumpSettings['databases']) {
274 $this->compressManager->write(
275 $this->typeAdapter->databases($this->dbName)
276 );
277 }
278
279
280 if (0 < count($this->dumpSettings['include-tables'])) {
281 $name = implode(",", $this->dumpSettings['include-tables']);
282 throw new Exception("Table (" . $name . ") not found in database");
283 }
284
285 $this->exportTables();
286 $this->exportViews();
287 $this->exportTriggers();
288 $this->exportProcedures();
289
290 $this->compressManager->write(
291 $this->typeAdapter->restore_parameters($this->dumpSettings)
292 );
293 $this->compressManager->write($this->getDumpFileFooter());
294
295 $this->compressManager->close();
296 }
297
298
299 private function getDumpFileHeader()
300 {
301 $header = '';
302 if ( !$this->dumpSettings['skip-comments'] ) {
303 // Some info about software, source and time
304 $header = "-- mysqldump-php https://github.com/ifsnop/mysqldump-php" . PHP_EOL .
305 "--" . PHP_EOL .
306 "-- Host: {$this->host}\tDatabase: {$this->dbName}" . PHP_EOL .
307 "-- ------------------------------------------------------" . PHP_EOL;
308
309 if ( !empty($this->version) ) {
310 $header .= "-- Server version \t" . $this->version . PHP_EOL;
311 }
312
313 if ( !$this->dumpSettings['skip-dump-date'] ) {
314 $header .= "-- Date: " . date('r') . PHP_EOL . PHP_EOL;
315 }
316 }
317 return $header;
318 }
319
320
321 private function getDumpFileFooter()
322 {
323 $footer = '';
324 if (!$this->dumpSettings['skip-comments']) {
325 $footer .= '-- Dump completed';
326 if (!$this->dumpSettings['skip-dump-date']) {
327 $footer .= ' on: ' . date('r');
328 }
329 $footer .= PHP_EOL;
330 }
331
332 return $footer;
333 }
334
335
336 private function getDatabaseStructure()
337 {
338 // Listing all tables from database
339 if (empty($this->dumpSettings['include-tables'])) {
340 // include all tables for now, blacklisting happens later
341 foreach ($this->dbHandler->query($this->typeAdapter->show_tables($this->dbName)) as $row) {
342 array_push($this->tables, current($row));
343 }
344 } else {
345 foreach ($this->dbHandler->query($this->typeAdapter->show_tables($this->dbName)) as $row) {
346 if (in_array(current($row), $this->dumpSettings['include-tables'], true)) {
347 array_push($this->tables, current($row));
348 $elem = array_search(
349 current($row),
350 $this->dumpSettings['include-tables']
351 );
352 unset($this->dumpSettings['include-tables'][$elem]);
353 }
354 }
355 }
356
357 // Listing all views from database
358 if (empty($this->dumpSettings['include-views'])) {
359 // include all views for now, blacklisting happens later
360 foreach ($this->dbHandler->query($this->typeAdapter->show_views($this->dbName)) as $row) {
361 array_push($this->views, current($row));
362 }
363 } else {
364 // include only the tables mentioned in include-tables
365 foreach ($this->dbHandler->query($this->typeAdapter->show_views($this->dbName)) as $row) {
366 if (in_array(current($row), $this->dumpSettings['include-views'], true)) {
367 array_push($this->views, current($row));
368 $elem = array_search(
369 current($row),
370 $this->dumpSettings['include-views']
371 );
372 unset($this->dumpSettings['include-views'][$elem]);
373 }
374 }
375 }
376
377 // Listing all triggers from database
378 if (false === $this->dumpSettings['skip-triggers']) {
379 foreach ($this->dbHandler->query($this->typeAdapter->show_triggers($this->dbName)) as $row) {
380 array_push($this->triggers, $row['Trigger']);
381 }
382 }
383
384 // Listing all procedures from database
385 if ($this->dumpSettings['routines']) {
386 foreach ($this->dbHandler->query($this->typeAdapter->show_procedures($this->dbName)) as $row) {
387 array_push($this->procedures, $row['procedure_name']);
388 }
389 }
390 }
391
392
393 private function matches($table, $arr) {
394 $match = false;
395
396 foreach ($arr as $pattern) {
397 if ( '/' != $pattern[0] ) {
398 continue;
399 }
400 if ( 1 == preg_match($pattern, $table) ) {
401 $match = true;
402 }
403 }
404
405 return in_array($table, $arr) || $match;
406 }
407
408 private function exportTables()
409 {
410 // Exporting tables one by one
411 foreach ($this->tables as $table) {
412 if ( $this->matches($table, $this->dumpSettings['exclude-tables']) ) {
413 continue;
414 }
415 $this->getTableStructure($table);
416 if ( false === $this->dumpSettings['no-data'] ) { // don't break compatibility with old trigger
417 $this->listValues($table);
418 } else if ( true === $this->dumpSettings['no-data']
419 || $this->matches($table, $this->dumpSettings['no-data']) ) {
420 continue;
421 } else {
422 $this->listValues($table);
423 }
424 }
425 }
426
427 private function exportViews()
428 {
429 if (false === $this->dumpSettings['no-create-info']) {
430 // Exporting views one by one
431 foreach ($this->views as $view) {
432 if ( $this->matches($view, $this->dumpSettings['exclude-tables']) ) {
433 continue;
434 }
435 $this->tableColumnTypes[$view] = $this->getTableColumnTypes($view);
436 $this->getViewStructureTable($view);
437 }
438 foreach ($this->views as $view) {
439 if ( $this->matches($view, $this->dumpSettings['exclude-tables']) ) {
440 continue;
441 }
442 $this->getViewStructureView($view);
443 }
444 }
445 }
446
447 /**
448 * Exports all the triggers found in database
449 *
450 * @return null
451 */
452 private function exportTriggers()
453 {
454 // Exporting triggers one by one
455 foreach ($this->triggers as $trigger) {
456 $this->getTriggerStructure($trigger);
457 }
458 }
459
460 /**
461 * Exports all the procedures found in database
462 *
463 * @return null
464 */
465 private function exportProcedures()
466 {
467 // Exporting triggers one by one
468 foreach ($this->procedures as $procedure) {
469 $this->getProcedureStructure($procedure);
470 }
471 }
472
473
474 private function getTableStructure($tableName)
475 {
476 if (!$this->dumpSettings['no-create-info']) {
477 $ret = '';
478 if (!$this->dumpSettings['skip-comments']) {
479 $ret = "--" . PHP_EOL .
480 "-- Table structure for table `$tableName`" . PHP_EOL .
481 "--" . PHP_EOL . PHP_EOL;
482 }
483 $stmt = $this->typeAdapter->show_create_table($tableName);
484 foreach ($this->dbHandler->query($stmt) as $r) {
485 $this->compressManager->write($ret);
486 if ($this->dumpSettings['add-drop-table']) {
487 $this->compressManager->write(
488 $this->typeAdapter->drop_table($tableName)
489 );
490 }
491 $this->compressManager->write(
492 $this->typeAdapter->create_table($r, $this->dumpSettings)
493 );
494 break;
495 }
496 }
497 $this->tableColumnTypes[$tableName] = $this->getTableColumnTypes($tableName);
498 return;
499 }
500
501
502 private function getTableColumnTypes($tableName) {
503 $columnTypes = array();
504 $columns = $this->dbHandler->query(
505 $this->typeAdapter->show_columns($tableName)
506 );
507 $columns->setFetchMode(PDO::FETCH_ASSOC);
508
509 foreach($columns as $key => $col) {
510 $types = $this->typeAdapter->parseColumnType($col);
511 $columnTypes[$col['Field']] = array(
512 'is_numeric'=> $types['is_numeric'],
513 'is_blob' => $types['is_blob'],
514 'type' => $types['type'],
515 'type_sql' => $col['Type']
516 );
517 }
518
519 return $columnTypes;
520 }
521
522
523 private function getViewStructureTable($viewName)
524 {
525 if (!$this->dumpSettings['skip-comments']) {
526 $ret = "--" . PHP_EOL .
527 "-- Stand-In structure for view `${viewName}`" . PHP_EOL .
528 "--" . PHP_EOL . PHP_EOL;
529 $this->compressManager->write($ret);
530 }
531 $stmt = $this->typeAdapter->show_create_view($viewName);
532
533 // create views as tables, to resolve dependencies
534 foreach ($this->dbHandler->query($stmt) as $r) {
535 if ($this->dumpSettings['add-drop-table']) {
536 $this->compressManager->write(
537 $this->typeAdapter->drop_view($viewName)
538 );
539 }
540
541 $this->compressManager->write(
542 $this->createStandInTable($viewName)
543 );
544 break;
545 }
546 }
547
548 function createStandInTable($viewName) {
549 $ret = array();
550 foreach($this->tableColumnTypes[$viewName] as $k => $v) {
551 $ret[] = "`${k}` ${v['type_sql']}";
552 }
553 $ret = implode(PHP_EOL . ",", $ret);
554
555 $ret = "CREATE TABLE IF NOT EXISTS `$viewName` (" .
556 PHP_EOL . $ret . PHP_EOL . ");" . PHP_EOL;
557
558 return $ret;
559 }
560
561
562 private function getViewStructureView($viewName)
563 {
564 if (!$this->dumpSettings['skip-comments']) {
565 $ret = "--" . PHP_EOL .
566 "-- View structure for view `${viewName}`" . PHP_EOL .
567 "--" . PHP_EOL . PHP_EOL;
568 $this->compressManager->write($ret);
569 }
570 $stmt = $this->typeAdapter->show_create_view($viewName);
571
572 foreach ($this->dbHandler->query($stmt) as $r) {
573 // because we must replace table with view, we should delete it
574 $this->compressManager->write(
575 $this->typeAdapter->drop_view($viewName)
576 );
577 $this->compressManager->write(
578 $this->typeAdapter->create_view($r)
579 );
580 break;
581 }
582 }
583
584
585 private function getTriggerStructure($triggerName)
586 {
587 $stmt = $this->typeAdapter->show_create_trigger($triggerName);
588 foreach ($this->dbHandler->query($stmt) as $r) {
589 if ($this->dumpSettings['add-drop-trigger']) {
590 $this->compressManager->write(
591 $this->typeAdapter->add_drop_trigger($triggerName)
592 );
593 }
594 $this->compressManager->write(
595 $this->typeAdapter->create_trigger($r)
596 );
597 return;
598 }
599 }
600
601
602 private function getProcedureStructure($procedureName)
603 {
604 if (!$this->dumpSettings['skip-comments']) {
605 $ret = "--" . PHP_EOL .
606 "-- Dumping routines for database '" . $this->dbName . "'" . PHP_EOL .
607 "--" . PHP_EOL . PHP_EOL;
608 $this->compressManager->write($ret);
609 }
610 $stmt = $this->typeAdapter->show_create_procedure($procedureName);
611 foreach ($this->dbHandler->query($stmt) as $r) {
612 $this->compressManager->write(
613 $this->typeAdapter->create_procedure($r, $this->dumpSettings)
614 );
615 return;
616 }
617 }
618
619
620 private function escape($tableName, $row)
621 {
622 $ret = array();
623 $columnTypes = $this->tableColumnTypes[$tableName];
624 foreach ($row as $colName => $colValue) {
625 if (is_null($colValue)) {
626 $ret[] = "NULL";
627 } elseif ($this->dumpSettings['hex-blob'] && $columnTypes[$colName]['is_blob']) {
628 if ($columnTypes[$colName]['type'] == 'bit' || !empty($colValue)) {
629 $ret[] = "0x${colValue}";
630 } else {
631 $ret[] = "''";
632 }
633 } elseif ($columnTypes[$colName]['is_numeric']) {
634 $ret[] = $colValue;
635 } else {
636 $ret[] = $this->dbHandler->quote($colValue);
637 }
638 }
639 return $ret;
640 }
641
642
643 private function listValues($tableName)
644 {
645 $this->prepareListValues($tableName);
646
647 $onlyOnce = true;
648 $lineSize = 0;
649
650 $colStmt = $this->getColumnStmt($tableName);
651 $stmt = "SELECT $colStmt FROM `$tableName`";
652
653 if ($this->dumpSettings['where']) {
654 $stmt .= " WHERE {$this->dumpSettings['where']}";
655 }
656 $resultSet = $this->dbHandler->query($stmt);
657 $resultSet->setFetchMode(PDO::FETCH_ASSOC);
658
659 foreach ($resultSet as $row) {
660 $vals = $this->escape($tableName, $row);
661 if ($onlyOnce || !$this->dumpSettings['extended-insert']) {
662
663 if ($this->dumpSettings['complete-insert']) {
664 $lineSize += $this->compressManager->write(
665 "INSERT INTO `$tableName` (`" .
666 implode("`, `", array_keys($this->tableColumnTypes[$tableName])) .
667 "`) VALUES (" . implode(",", $vals) . ")"
668 );
669 } else {
670 $lineSize += $this->compressManager->write(
671 "INSERT INTO `$tableName` VALUES (" . implode(",", $vals) . ")"
672 );
673 }
674 $onlyOnce = false;
675 } else {
676 $lineSize += $this->compressManager->write(",(" . implode(",", $vals) . ")");
677 }
678 if (($lineSize > $this->dumpSettings['net_buffer_length']) ||
679 !$this->dumpSettings['extended-insert']) {
680 $onlyOnce = true;
681 $lineSize = $this->compressManager->write(";" . PHP_EOL);
682 }
683 }
684 $resultSet->closeCursor();
685
686 if (!$onlyOnce) {
687 $this->compressManager->write(";" . PHP_EOL);
688 }
689
690 $this->endListValues($tableName);
691 }
692
693 function prepareListValues($tableName)
694 {
695 if (!$this->dumpSettings['skip-comments']) {
696 $this->compressManager->write(
697 "--" . PHP_EOL .
698 "-- Dumping data for table `$tableName`" . PHP_EOL .
699 "--" . PHP_EOL . PHP_EOL
700 );
701 }
702
703 if ($this->dumpSettings['single-transaction']) {
704 $this->dbHandler->exec($this->typeAdapter->setup_transaction());
705 $this->dbHandler->exec($this->typeAdapter->start_transaction());
706 }
707
708 if ($this->dumpSettings['lock-tables']) {
709 $this->typeAdapter->lock_table($tableName);
710 }
711
712 if ($this->dumpSettings['add-locks']) {
713 $this->compressManager->write(
714 $this->typeAdapter->start_add_lock_table($tableName)
715 );
716 }
717
718 if ($this->dumpSettings['disable-keys']) {
719 $this->compressManager->write(
720 $this->typeAdapter->start_add_disable_keys($tableName)
721 );
722 }
723
724 if ($this->dumpSettings['no-autocommit']) {
725 $this->compressManager->write(
726 $this->typeAdapter->start_disable_autocommit()
727 );
728 }
729
730 return;
731 }
732
733 function endListValues($tableName)
734 {
735 if ($this->dumpSettings['disable-keys']) {
736 $this->compressManager->write(
737 $this->typeAdapter->end_add_disable_keys($tableName)
738 );
739 }
740
741 if ($this->dumpSettings['add-locks']) {
742 $this->compressManager->write(
743 $this->typeAdapter->end_add_lock_table($tableName)
744 );
745 }
746
747 if ($this->dumpSettings['single-transaction']) {
748 $this->dbHandler->exec($this->typeAdapter->commit_transaction());
749 }
750
751 if ($this->dumpSettings['lock-tables']) {
752 $this->typeAdapter->unlock_table($tableName);
753 }
754
755 // Commit to enable autocommit
756 if ($this->dumpSettings['no-autocommit']) {
757 $this->compressManager->write(
758 $this->typeAdapter->end_disable_autocommit()
759 );
760 }
761
762 $this->compressManager->write(PHP_EOL);
763
764 return;
765 }
766
767
768 function getColumnStmt($tableName)
769 {
770 $colStmt = array();
771 foreach($this->tableColumnTypes[$tableName] as $colName => $colType) {
772 if ($colType['type'] == 'bit' && $this->dumpSettings['hex-blob']) {
773 $colStmt[] = "LPAD(HEX(`${colName}`),2,'0') AS `${colName}`";
774 } else if ($colType['is_blob'] && $this->dumpSettings['hex-blob']) {
775 $colStmt[] = "HEX(`${colName}`) AS `${colName}`";
776 } else {
777 $colStmt[] = "`${colName}`";
778 }
779 }
780 $colStmt = implode($colStmt, ",");
781
782 return $colStmt;
783 }
784}
785
786
787abstract class CompressMethod
788{
789 public static $enums = array(
790 "None",
791 "Gzip",
792 "Bzip2"
793 );
794
795 /**
796 * @param string $c
797 * @return boolean
798 */
799 public static function isValid($c)
800 {
801 return in_array($c, self::$enums);
802 }
803}
804
805abstract class CompressManagerFactory
806{
807 /**
808 * @param string $c
809 * @return CompressBzip2|CompressGzip|CompressNone
810 */
811 public static function create($c)
812 {
813 $c = ucfirst(strtolower($c));
814 if (! CompressMethod::isValid($c)) {
815 throw new Exception("Compression method ($c) is not defined yet");
816 }
817
818 $method = __NAMESPACE__ . "\\" . "Compress" . $c;
819
820 return new $method;
821 }
822}
823
824class CompressBzip2 extends CompressManagerFactory
825{
826 private $fileHandler = null;
827
828 public function __construct()
829 {
830 if (! function_exists("bzopen")) {
831 throw new Exception("Compression is enabled, but bzip2 lib is not installed or configured properly");
832 }
833 }
834
835 /**
836 * @param string $filename
837 */
838 public function open($filename)
839 {
840 $this->fileHandler = bzopen($filename, "w");
841 if (false === $this->fileHandler) {
842 throw new Exception("Output file is not writable");
843 }
844
845 return true;
846 }
847
848 public function write($str)
849 {
850 if (false === ($bytesWritten = bzwrite($this->fileHandler, $str))) {
851 throw new Exception("Writting to file failed! Probably, there is no more free space left?");
852 }
853 return $bytesWritten;
854 }
855
856 public function close()
857 {
858 return bzclose($this->fileHandler);
859 }
860}
861
862class CompressGzip extends CompressManagerFactory
863{
864 private $fileHandler = null;
865
866 public function __construct()
867 {
868 if (! function_exists("gzopen")) {
869 throw new Exception("Compression is enabled, but gzip lib is not installed or configured properly");
870 }
871 }
872
873 /**
874 * @param string $filename
875 */
876 public function open($filename)
877 {
878 $this->fileHandler = gzopen($filename, "wb");
879 if (false === $this->fileHandler) {
880 throw new Exception("Output file is not writable");
881 }
882
883 return true;
884 }
885
886 public function write($str)
887 {
888 if (false === ($bytesWritten = gzwrite($this->fileHandler, $str))) {
889 throw new Exception("Writting to file failed! Probably, there is no more free space left?");
890 }
891 return $bytesWritten;
892 }
893
894 public function close()
895 {
896 return gzclose($this->fileHandler);
897 }
898}
899
900class CompressNone extends CompressManagerFactory
901{
902 private $fileHandler = null;
903
904 /**
905 * @param string $filename
906 */
907 public function open($filename)
908 {
909 $this->fileHandler = fopen($filename, "wb");
910 if (false === $this->fileHandler) {
911 throw new Exception("Output file is not writable");
912 }
913
914 return true;
915 }
916
917 public function write($str)
918 {
919 if (false === ($bytesWritten = fwrite($this->fileHandler, $str))) {
920 throw new Exception("Writting to file failed! Probably, there is no more free space left?");
921 }
922 return $bytesWritten;
923 }
924
925 public function close()
926 {
927 return fclose($this->fileHandler);
928 }
929}
930
931/**
932 * Enum with all available TypeAdapter implementations
933 *
934 */
935abstract class TypeAdapter
936{
937 public static $enums = array(
938 "Sqlite",
939 "Mysql"
940 );
941
942 /**
943 * @param string $c
944 * @return boolean
945 */
946 public static function isValid($c)
947 {
948 return in_array($c, self::$enums);
949 }
950}
951
952/**
953 * TypeAdapter Factory
954 *
955 */
956abstract class TypeAdapterFactory
957{
958 /**
959 * @param string $c Type of database factory to create (Mysql, Sqlite,...)
960 * @param PDO $dbHandler
961 */
962 public static function create($c, $dbHandler = null)
963 {
964 $c = ucfirst(strtolower($c));
965 if (! TypeAdapter::isValid($c)) {
966 throw new Exception("Database type support for ($c) not yet available");
967 }
968 $method = __NAMESPACE__ . "\\" . "TypeAdapter" . $c;
969 return new $method($dbHandler);
970 }
971
972 /**
973 * function databases Add sql to create and use database
974 * @todo make it do something with sqlite
975 */
976 public function databases()
977 {
978 return "";
979 }
980
981 public function show_create_table($tableName)
982 {
983 return "SELECT tbl_name as 'Table', sql as 'Create Table' " .
984 "FROM sqlite_master " .
985 "WHERE type='table' AND tbl_name='$tableName'";
986 }
987
988 /**
989 * function create_table Get table creation code from database
990 * @todo make it do something with sqlite
991 */
992 public function create_table($row, $dumpSettings)
993 {
994 return "";
995 }
996
997 public function show_create_view($viewName)
998 {
999 return "SELECT tbl_name as 'View', sql as 'Create View' " .
1000 "FROM sqlite_master " .
1001 "WHERE type='view' AND tbl_name='$viewName'";
1002 }
1003
1004 /**
1005 * function create_view Get view creation code from database
1006 * @todo make it do something with sqlite
1007 */
1008 public function create_view($row)
1009 {
1010 return "";
1011 }
1012
1013
1014 public function show_create_trigger($triggerName)
1015 {
1016 return "";
1017 }
1018
1019
1020 public function create_trigger($triggerName)
1021 {
1022 return "";
1023 }
1024
1025 /**
1026 * function create_procedure Modify procedure code, add delimiters, etc
1027 * @todo make it do something with sqlite
1028 */
1029 public function create_procedure($procedureName, $dumpSettings)
1030 {
1031 return "";
1032 }
1033
1034 public function show_tables()
1035 {
1036 return "SELECT tbl_name FROM sqlite_master WHERE type='table'";
1037 }
1038
1039 public function show_views()
1040 {
1041 return "SELECT tbl_name FROM sqlite_master WHERE type='view'";
1042 }
1043
1044 public function show_triggers()
1045 {
1046 return "SELECT name FROM sqlite_master WHERE type='trigger'";
1047 }
1048
1049 public function show_columns()
1050 {
1051 if (func_num_args() != 1) {
1052 return "";
1053 }
1054
1055 $args = func_get_args();
1056
1057 return "pragma table_info(${args[0]})";
1058 }
1059
1060 public function show_procedures()
1061 {
1062 return "";
1063 }
1064
1065 public function setup_transaction()
1066 {
1067 return "";
1068 }
1069
1070 public function start_transaction()
1071 {
1072 return "BEGIN EXCLUSIVE";
1073 }
1074
1075 public function commit_transaction()
1076 {
1077 return "COMMIT";
1078 }
1079
1080 public function lock_table()
1081 {
1082 return "";
1083 }
1084
1085 public function unlock_table()
1086 {
1087 return "";
1088 }
1089
1090 public function start_add_lock_table()
1091 {
1092 return PHP_EOL;
1093 }
1094
1095 public function end_add_lock_table()
1096 {
1097 return PHP_EOL;
1098 }
1099
1100 public function start_add_disable_keys()
1101 {
1102 return PHP_EOL;
1103 }
1104
1105 public function end_add_disable_keys()
1106 {
1107 return PHP_EOL;
1108 }
1109
1110 public function start_disable_foreign_keys_check()
1111 {
1112 return PHP_EOL;
1113 }
1114
1115 public function end_disable_foreign_keys_check()
1116 {
1117 return PHP_EOL;
1118 }
1119
1120 public function add_drop_database()
1121 {
1122 return PHP_EOL;
1123 }
1124
1125 public function add_drop_trigger()
1126 {
1127 return PHP_EOL;
1128 }
1129
1130 public function drop_table()
1131 {
1132 return PHP_EOL;
1133 }
1134
1135 public function drop_view()
1136 {
1137 return PHP_EOL;
1138 }
1139
1140 public function parseColumnType($colType)
1141 {
1142 return array();
1143 }
1144
1145 public function backup_parameters()
1146 {
1147 return PHP_EOL;
1148 }
1149
1150 public function restore_parameters()
1151 {
1152 return PHP_EOL;
1153 }
1154}
1155
1156class TypeAdapterPgsql extends TypeAdapterFactory
1157{
1158}
1159
1160class TypeAdapterDblib extends TypeAdapterFactory
1161{
1162}
1163
1164class TypeAdapterSqlite extends TypeAdapterFactory
1165{
1166}
1167
1168class TypeAdapterMysql extends TypeAdapterFactory
1169{
1170
1171 private $dbHandler = null;
1172
1173
1174 public $mysqlTypes = array(
1175 'numerical' => array(
1176 'bit',
1177 'tinyint',
1178 'smallint',
1179 'mediumint',
1180 'int',
1181 'integer',
1182 'bigint',
1183 'real',
1184 'double',
1185 'float',
1186 'decimal',
1187 'numeric'
1188 ),
1189 'blob' => array(
1190 'tinyblob',
1191 'blob',
1192 'mediumblob',
1193 'longblob',
1194 'binary',
1195 'varbinary',
1196 'bit',
1197 'geometry', /* http://bugs.mysql.com/bug.php?id=43544 */
1198 'point',
1199 'linestring',
1200 'polygon',
1201 'multipoint',
1202 'multilinestring',
1203 'multipolygon',
1204 'geometrycollection',
1205 )
1206 );
1207
1208 public function __construct ($dbHandler)
1209 {
1210 $this->dbHandler = $dbHandler;
1211 }
1212
1213 public function databases()
1214 {
1215 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1216 $args = func_get_args();
1217 $databaseName = $args[0];
1218
1219 $resultSet = $this->dbHandler->query("SHOW VARIABLES LIKE 'character_set_database';");
1220 $characterSet = $resultSet->fetchColumn(1);
1221 $resultSet->closeCursor();
1222
1223 $resultSet = $this->dbHandler->query("SHOW VARIABLES LIKE 'collation_database';");
1224 $collationDb = $resultSet->fetchColumn(1);
1225 $resultSet->closeCursor();
1226 $ret = "";
1227
1228 $ret .= "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `${databaseName}`".
1229 " /*!40100 DEFAULT CHARACTER SET ${characterSet} " .
1230 " COLLATE ${collationDb} */;" . PHP_EOL . PHP_EOL .
1231 "USE `${databaseName}`;" . PHP_EOL . PHP_EOL;
1232
1233 return $ret;
1234 }
1235
1236 public function show_create_table($tableName)
1237 {
1238 return "SHOW CREATE TABLE `$tableName`";
1239 }
1240
1241 public function show_create_view($viewName)
1242 {
1243 return "SHOW CREATE VIEW `$viewName`";
1244 }
1245
1246 public function show_create_trigger($triggerName)
1247 {
1248 return "SHOW CREATE TRIGGER `$triggerName`";
1249 }
1250
1251 public function show_create_procedure($procedureName)
1252 {
1253 return "SHOW CREATE PROCEDURE `$procedureName`";
1254 }
1255
1256 public function create_table( $row, $dumpSettings )
1257 {
1258 if ( !isset($row['Create Table']) ) {
1259 throw new Exception("Error getting table code, unknown output");
1260 }
1261
1262 $createTable = $row['Create Table'];
1263 if ( $dumpSettings['reset-auto-increment'] ) {
1264 $match = "/AUTO_INCREMENT=[0-9]+/s";
1265 $replace = "";
1266 $createTable = preg_replace($match, $replace, $createTable);
1267 }
1268
1269 $ret = "/*!40101 SET @saved_cs_client = @@character_set_client */;" . PHP_EOL .
1270 "/*!40101 SET character_set_client = " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL .
1271 $createTable . ";" . PHP_EOL .
1272 "/*!40101 SET character_set_client = @saved_cs_client */;" . PHP_EOL .
1273 PHP_EOL;
1274 return $ret;
1275 }
1276
1277 public function create_view($row)
1278 {
1279 $ret = "";
1280 if (!isset($row['Create View'])) {
1281 throw new Exception("Error getting view structure, unknown output");
1282 }
1283
1284 $triggerStmt = $row['Create View'];
1285
1286 $triggerStmtReplaced1 = str_replace(
1287 "CREATE ALGORITHM",
1288 "/*!50001 CREATE ALGORITHM",
1289 $triggerStmt
1290 );
1291 $triggerStmtReplaced2 = str_replace(
1292 " DEFINER=",
1293 " */" . PHP_EOL . "/*!50013 DEFINER=",
1294 $triggerStmtReplaced1
1295 );
1296 $triggerStmtReplaced3 = str_replace(
1297 " VIEW ",
1298 " */" . PHP_EOL . "/*!50001 VIEW ",
1299 $triggerStmtReplaced2
1300 );
1301 if (false === $triggerStmtReplaced1 ||
1302 false === $triggerStmtReplaced2 ||
1303 false === $triggerStmtReplaced3) {
1304 $triggerStmtReplaced = $triggerStmt;
1305 } else {
1306 $triggerStmtReplaced = $triggerStmtReplaced3 . " */;";
1307 }
1308
1309 $ret .= $triggerStmtReplaced . PHP_EOL . PHP_EOL;
1310 return $ret;
1311 }
1312
1313 public function create_trigger($row)
1314 {
1315 $ret = "";
1316 if (!isset($row['SQL Original Statement'])) {
1317 throw new Exception("Error getting trigger code, unknown output");
1318 }
1319
1320 $triggerStmt = $row['SQL Original Statement'];
1321 $triggerStmtReplaced = str_replace(
1322 "CREATE DEFINER",
1323 "/*!50003 CREATE*/ /*!50017 DEFINER",
1324 $triggerStmt
1325 );
1326 $triggerStmtReplaced = str_replace(
1327 " TRIGGER",
1328 "*/ /*!50003 TRIGGER",
1329 $triggerStmtReplaced
1330 );
1331 if ( false === $triggerStmtReplaced ) {
1332 $triggerStmtReplaced = $triggerStmt;
1333 }
1334
1335 $ret .= "DELIMITER ;;" . PHP_EOL .
1336 $triggerStmtReplaced . "*/;;" . PHP_EOL .
1337 "DELIMITER ;" . PHP_EOL . PHP_EOL;
1338 return $ret;
1339 }
1340
1341 public function create_procedure($row, $dumpSettings)
1342 {
1343 $ret = "";
1344 if (!isset($row['Create Procedure'])) {
1345 throw new Exception("Error getting procedure code, unknown output. " .
1346 "Please check 'https://bugs.mysql.com/bug.php?id=14564'");
1347 }
1348 $procedureStmt = $row['Create Procedure'];
1349
1350 $ret .= "/*!50003 DROP PROCEDURE IF EXISTS `" .
1351 $row['Procedure'] . "` */;" . PHP_EOL .
1352 "/*!40101 SET @saved_cs_client = @@character_set_client */;" . PHP_EOL .
1353 "/*!40101 SET character_set_client = " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL .
1354 "DELIMITER ;;" . PHP_EOL .
1355 $procedureStmt . " ;;" . PHP_EOL .
1356 "DELIMITER ;" . PHP_EOL .
1357 "/*!40101 SET character_set_client = @saved_cs_client */;" . PHP_EOL . PHP_EOL;
1358
1359 return $ret;
1360 }
1361
1362 public function show_tables()
1363 {
1364 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1365 $args = func_get_args();
1366 return "SELECT TABLE_NAME AS tbl_name " .
1367 "FROM INFORMATION_SCHEMA.TABLES " .
1368 "WHERE TABLE_TYPE='BASE TABLE' AND TABLE_SCHEMA='${args[0]}'";
1369 }
1370
1371 public function show_views()
1372 {
1373 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1374 $args = func_get_args();
1375 return "SELECT TABLE_NAME AS tbl_name " .
1376 "FROM INFORMATION_SCHEMA.TABLES " .
1377 "WHERE TABLE_TYPE='VIEW' AND TABLE_SCHEMA='${args[0]}'";
1378 }
1379
1380 public function show_triggers()
1381 {
1382 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1383 $args = func_get_args();
1384 return "SHOW TRIGGERS FROM `${args[0]}`;";
1385 }
1386
1387 public function show_columns()
1388 {
1389 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1390 $args = func_get_args();
1391 return "SHOW COLUMNS FROM `${args[0]}`;";
1392 }
1393
1394 public function show_procedures()
1395 {
1396 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1397 $args = func_get_args();
1398 return "SELECT SPECIFIC_NAME AS procedure_name " .
1399 "FROM INFORMATION_SCHEMA.ROUTINES " .
1400 "WHERE ROUTINE_TYPE='PROCEDURE' AND ROUTINE_SCHEMA='${args[0]}'";
1401 }
1402
1403 public function setup_transaction()
1404 {
1405 return "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ";
1406 }
1407
1408 public function start_transaction()
1409 {
1410 return "START TRANSACTION";
1411 }
1412
1413 public function commit_transaction()
1414 {
1415 return "COMMIT";
1416 }
1417
1418 public function lock_table()
1419 {
1420 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1421 $args = func_get_args();
1422 return $this->dbHandler->exec("LOCK TABLES `${args[0]}` READ LOCAL");
1423
1424 }
1425
1426 public function unlock_table()
1427 {
1428 return $this->dbHandler->exec("UNLOCK TABLES");
1429 }
1430
1431 public function start_add_lock_table()
1432 {
1433 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1434 $args = func_get_args();
1435 return "LOCK TABLES `${args[0]}` WRITE;" . PHP_EOL;
1436 }
1437
1438 public function end_add_lock_table()
1439 {
1440 return "UNLOCK TABLES;" . PHP_EOL;
1441 }
1442
1443 public function start_add_disable_keys()
1444 {
1445 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1446 $args = func_get_args();
1447 return "/*!40000 ALTER TABLE `${args[0]}` DISABLE KEYS */;" .
1448 PHP_EOL;
1449 }
1450
1451 public function end_add_disable_keys()
1452 {
1453 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1454 $args = func_get_args();
1455 return "/*!40000 ALTER TABLE `${args[0]}` ENABLE KEYS */;" .
1456 PHP_EOL;
1457 }
1458
1459 public function start_disable_autocommit()
1460 {
1461 return "SET autocommit=0;" . PHP_EOL;
1462 }
1463
1464 public function end_disable_autocommit()
1465 {
1466 return "COMMIT;" . PHP_EOL;
1467 }
1468
1469 public function add_drop_database()
1470 {
1471 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1472 $args = func_get_args();
1473 return "/*!40000 DROP DATABASE IF EXISTS `${args[0]}`*/;" .
1474 PHP_EOL . PHP_EOL;
1475 }
1476
1477 public function add_drop_trigger()
1478 {
1479 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1480 $args = func_get_args();
1481 return "DROP TRIGGER IF EXISTS `${args[0]}`;" . PHP_EOL;
1482 }
1483
1484 public function drop_table()
1485 {
1486 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1487 $args = func_get_args();
1488 return "DROP TABLE IF EXISTS `${args[0]}`;" . PHP_EOL;
1489 }
1490
1491 public function drop_view()
1492 {
1493 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1494 $args = func_get_args();
1495 return "DROP TABLE IF EXISTS `${args[0]}`;" . PHP_EOL .
1496 "/*!50001 DROP VIEW IF EXISTS `${args[0]}`*/;" . PHP_EOL;
1497 }
1498
1499 public function getDatabaseHeader()
1500 {
1501 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1502 $args = func_get_args();
1503 return "--" . PHP_EOL .
1504 "-- Current Database: `${args[0]}`" . PHP_EOL .
1505 "--" . PHP_EOL . PHP_EOL;
1506 }
1507
1508 /**
1509 * Decode column metadata and fill info structure.
1510 * type, is_numeric and is_blob will always be available.
1511 *
1512 * @param array $colType Array returned from "SHOW COLUMNS FROM tableName"
1513 * @return array
1514 */
1515 public function parseColumnType($colType)
1516 {
1517 $colInfo = array();
1518 $colParts = explode(" ", $colType['Type']);
1519
1520 if($fparen = strpos($colParts[0], "("))
1521 {
1522 $colInfo['type'] = substr($colParts[0], 0, $fparen);
1523 $colInfo['length'] = str_replace(")", "", substr($colParts[0], $fparen+1));
1524 $colInfo['attributes'] = isset($colParts[1]) ? $colParts[1] : NULL;
1525 }
1526 else
1527 {
1528 $colInfo['type'] = $colParts[0];
1529 }
1530 $colInfo['is_numeric'] = in_array($colInfo['type'], $this->mysqlTypes['numerical']);
1531 $colInfo['is_blob'] = in_array($colInfo['type'], $this->mysqlTypes['blob']);
1532
1533 return $colInfo;
1534 }
1535
1536 public function backup_parameters()
1537 {
1538 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1539 $args = func_get_args();
1540 $dumpSettings = $args[0];
1541 $ret = "/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;" . PHP_EOL .
1542 "/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;" . PHP_EOL .
1543 "/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;" . PHP_EOL .
1544 "/*!40101 SET NAMES " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL;
1545
1546 if (false === $dumpSettings['skip-tz-utc']) {
1547 $ret .= "/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;" . PHP_EOL .
1548 "/*!40103 SET TIME_ZONE='+00:00' */;" . PHP_EOL;
1549 }
1550
1551 $ret .= "/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;" . PHP_EOL .
1552 "/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;" . PHP_EOL .
1553 "/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;" . PHP_EOL .
1554 "/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;" . PHP_EOL .PHP_EOL;
1555
1556 return $ret;
1557 }
1558
1559 public function restore_parameters()
1560 {
1561 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1562 $args = func_get_args();
1563 $dumpSettings = $args[0];
1564 $ret = "";
1565
1566 if (false === $dumpSettings['skip-tz-utc']) {
1567 $ret .= "/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;" . PHP_EOL;
1568 }
1569
1570 $ret .= "/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;" . PHP_EOL .
1571 "/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;" . PHP_EOL .
1572 "/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;" . PHP_EOL .
1573 "/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;" . PHP_EOL .
1574 "/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;" . PHP_EOL .
1575 "/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;" . PHP_EOL .
1576 "/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;" . PHP_EOL . PHP_EOL;
1577
1578 return $ret;
1579 }
1580
1581
1582 private function check_parameters($num_args, $expected_num_args, $method_name)
1583 {
1584 if ( $num_args != $expected_num_args ) {
1585 throw new Exception("Unexpected parameter passed to $method_name");
1586 }
1587 return;
1588 }
1589}
1590?>