PluginProbe
WP Reset / 1.40
WP Reset v1.40
trunk 1.0 1.1 1.11 1.20 1.25 1.30 1.35 1.40 1.45 1.50 1.55 1.60 1.65 1.70 1.75 1.77 1.80 1.81 1.82 1.83 1.84 1.85 1.86 1.90 All 42 releases
wp-reset / libs / dumper.php

dumper.php in WP Reset 1.40, at libs/dumper.php

515 lines 13.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Abstract dump file: provides common interface for writing
4 * data to dump files.
5 * (c) 2create Studio, Bulgaria
6 * http://2create.bg/
7 */
8 abstract class Shuttle_Dump_File {
9 /**
10 * File Handle
11 */
12 protected $fh;
13
14 /**
15 * Location of the dump file on the disk
16 */
17 protected $file_location;
18
19 abstract function write($string);
20 abstract function end();
21
22 static function create($filename) {
23 if (self::is_gzip($filename)) {
24 return new Shuttle_Dump_File_Gzip($filename);
25 }
26 return new Shuttle_Dump_File_Plaintext($filename);
27 }
28 function __construct($file) {
29 $this->file_location = $file;
30 $this->fh = $this->open();
31
32 if (!$this->fh) {
33 throw new Shuttle_Exception("Couldn't create gz file");
34 }
35 }
36
37 public static function is_gzip($filename) {
38 return preg_match('~gz$~i', $filename);
39 }
40 }
41
42 /**
43 * Plain text implementation. Uses standard file functions in PHP.
44 */
45 class Shuttle_Dump_File_Plaintext extends Shuttle_Dump_File {
46 function open() {
47 return fopen($this->file_location, 'w');
48 }
49 function write($string) {
50 return fwrite($this->fh, $string);
51 }
52 function end() {
53 return fclose($this->fh);
54 }
55 }
56
57 /**
58 * Gzip implementation. Uses gz* functions.
59 */
60 class Shuttle_Dump_File_Gzip extends Shuttle_Dump_File {
61 function open() {
62 return gzopen($this->file_location, 'wb9');
63 }
64 function write($string) {
65 return gzwrite($this->fh, $string);
66 }
67 function end() {
68 return gzclose($this->fh);
69 }
70 }
71
72 /**
73 * MySQL insert statement builder.
74 */
75 class Shuttle_Insert_Statement {
76 private $rows = array();
77 private $length = 0;
78 private $table;
79
80 function __construct($table) {
81 $this->table = $table;
82 }
83
84 function reset() {
85 $this->rows = array();
86 $this->length = 0;
87 }
88
89 function add_row($row) {
90 $row = '(' . implode(",", $row) . ')';
91 $this->rows[] = $row;
92 $this->length += strlen($row);
93 }
94
95 function get_sql() {
96 if (empty($this->rows)) {
97 return false;
98 }
99
100 return 'INSERT INTO `' . $this->table . '` VALUES ' .
101 implode(",\n", $this->rows) . '; ';
102 }
103
104 function get_length() {
105 return $this->length;
106 }
107 }
108
109 /**
110 * Main facade
111 */
112 abstract class Shuttle_Dumper {
113 /**
114 * Maximum length of single insert statement
115 */
116 const INSERT_THRESHOLD = 838860;
117
118 /**
119 * @var Shuttle_DBConn
120 */
121 public $db;
122
123 /**
124 * @var Shuttle_Dump_File
125 */
126 public $dump_file;
127
128 /**
129 * End of line style used in the dump
130 */
131 public $eol = "\r\n";
132
133 /**
134 * Specificed tables to include
135 */
136 public $include_tables;
137
138 /**
139 * Specified tables to exclude
140 */
141 public $exclude_tables = array();
142
143 /**
144 * Factory method for dumper on current hosts's configuration.
145 */
146 static function create($db_options) {
147 $db = Shuttle_DBConn::create($db_options);
148
149 $db->connect();
150
151 if (self::has_shell_access()
152 && self::is_shell_command_available('mysqldump')
153 && self::is_shell_command_available('gzip')
154 ) {
155 $dumper = new Shuttle_Dumper_ShellCommand($db);
156 } else {
157 $dumper = new Shuttle_Dumper_Native($db);
158 }
159
160 if (isset($db_options['include_tables'])) {
161 $dumper->include_tables = $db_options['include_tables'];
162 }
163 if (isset($db_options['exclude_tables'])) {
164 $dumper->exclude_tables = $db_options['exclude_tables'];
165 }
166
167 return $dumper;
168 }
169
170 function __construct(Shuttle_DBConn $db) {
171 $this->db = $db;
172 }
173
174 public static function has_shell_access() {
175 if (!is_callable('shell_exec')) {
176 return false;
177 }
178 $disabled_functions = ini_get('disable_functions');
179 return stripos($disabled_functions, 'shell_exec') === false;
180 }
181
182 public static function is_shell_command_available($command) {
183 if (preg_match('~win~i', PHP_OS)) {
184 /*
185 On Windows, the `where` command checks for availabilty in PATH. According
186 to the manual(`where /?`), there is quiet mode:
187 ....
188 /Q Returns only the exit code, without displaying the list
189 of matched files. (Quiet mode)
190 ....
191 */
192 $output = array();
193 exec('where /Q ' . $command, $output, $return_val);
194
195 if (intval($return_val) === 1) {
196 return false;
197 } else {
198 return true;
199 }
200
201 } else {
202 $last_line = exec('which ' . $command);
203 $last_line = trim($last_line);
204
205 // Whenever there is at least one line in the output,
206 // it should be the path to the executable
207 if (empty($last_line)) {
208 return false;
209 } else {
210 return true;
211 }
212 }
213
214 }
215
216 /**
217 * Create an export file from the tables with that prefix.
218 * @param string $export_file_location the file to put the dump to.
219 * Note that whenever the file has .gz extension the dump will be comporessed with gzip
220 * @param string $table_prefix Allow to export only tables with particular prefix
221 * @return void
222 */
223 abstract public function dump($export_file_location, $table_prefix='');
224
225 protected function get_tables($table_prefix) {
226 if (!empty($this->include_tables)) {
227 return $this->include_tables;
228 }
229
230 // $tables will only include the tables and not views.
231 // TODO - Handle views also, edits to be made in function 'get_create_table_sql' line 336
232 $tables = $this->db->fetch_numeric('
233 SHOW FULL TABLES WHERE Table_Type = "BASE TABLE" AND Tables_in_'.$this->db->name.' LIKE "' . $this->db->escape_like($table_prefix) . '%"
234 ');
235
236 $tables_list = array();
237 foreach ($tables as $table_row) {
238 $table_name = $table_row[0];
239 if (!in_array($table_name, $this->exclude_tables)) {
240 $tables_list[] = $table_name;
241 }
242 }
243 return $tables_list;
244 }
245 }
246
247 class Shuttle_Dumper_ShellCommand extends Shuttle_Dumper {
248 function dump($export_file_location, $table_prefix='') {
249 $command = 'mysqldump -h ' . escapeshellarg($this->db->host) .
250 ' -u ' . escapeshellarg($this->db->username) .
251 ' --password=' . escapeshellarg($this->db->password) .
252 ' ' . escapeshellarg($this->db->name);
253
254 $include_all_tables = empty($table_prefix) &&
255 empty($this->include_tables) &&
256 empty($this->exclude_tables);
257
258 if (!$include_all_tables) {
259 $tables = $this->get_tables($table_prefix);
260 $command .= ' ' . implode(' ', array_map('escapeshellarg', $tables));
261 }
262
263 $error_file = tempnam(sys_get_temp_dir(), 'err');
264
265 $command .= ' 2> ' . escapeshellarg($error_file);
266
267 if (Shuttle_Dump_File::is_gzip($export_file_location)) {
268 $command .= ' | gzip';
269 }
270
271 $command .= ' > ' . escapeshellarg($export_file_location);
272
273 exec($command, $output, $return_val);
274
275 if ($return_val !== 0) {
276 $error_text = file_get_contents($error_file);
277 unlink($error_file);
278 throw new Shuttle_Exception('Couldn\'t export database: ' . $error_text);
279 }
280
281 unlink($error_file);
282 }
283 }
284
285 class Shuttle_Dumper_Native extends Shuttle_Dumper {
286 public function dump($export_file_location, $table_prefix='') {
287 $eol = $this->eol;
288
289 $this->dump_file = Shuttle_Dump_File::create($export_file_location);
290
291 $this->dump_file->write("-- Generation time: " . date('r') . $eol);
292 $this->dump_file->write("-- Host: " . $this->db->host . $eol);
293 $this->dump_file->write("-- DB name: " . $this->db->name . $eol);
294 $this->dump_file->write("/*!40030 SET NAMES UTF8 */;$eol");
295
296 $this->dump_file->write("/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;$eol");
297 $this->dump_file->write("/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;$eol");
298 $this->dump_file->write("/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;$eol");
299 $this->dump_file->write("/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;$eol");
300 $this->dump_file->write("/*!40103 SET TIME_ZONE='+00:00' */;$eol");
301 $this->dump_file->write("/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;$eol");
302 $this->dump_file->write("/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;$eol");
303 $this->dump_file->write("/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;$eol");
304 $this->dump_file->write("/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;$eol$eol");
305
306
307 $tables = $this->get_tables($table_prefix);
308 foreach ($tables as $table) {
309 $this->dump_table($table);
310 }
311
312 $this->dump_file->write("$eol$eol");
313 $this->dump_file->write("/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;$eol");
314 $this->dump_file->write("/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;$eol");
315 $this->dump_file->write("/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;$eol");
316 $this->dump_file->write("/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;$eol");
317 $this->dump_file->write("/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;$eol");
318 $this->dump_file->write("/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;$eol");
319 $this->dump_file->write("/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;$eol$eol");
320
321 unset($this->dump_file);
322 }
323
324 protected function dump_table($table) {
325 $eol = $this->eol;
326
327 $this->dump_file->write("DROP TABLE IF EXISTS `$table`;$eol");
328
329 $create_table_sql = $this->get_create_table_sql($table);
330 $this->dump_file->write($create_table_sql . $eol . $eol);
331
332 $data = $this->db->query("SELECT * FROM `$table`");
333
334 $insert = new Shuttle_Insert_Statement($table);
335
336 while ($row = $this->db->fetch_row($data)) {
337 $row_values = array();
338 foreach ($row as $value) {
339 $row_values[] = $this->db->escape($value);
340 }
341 $insert->add_row( $row_values );
342
343 if ($insert->get_length() > self::INSERT_THRESHOLD) {
344 // The insert got too big: write the SQL and create
345 // new insert statement
346 $this->dump_file->write($insert->get_sql() . $eol);
347 $insert->reset();
348 }
349 }
350
351 $sql = $insert->get_sql();
352 if ($sql) {
353 $this->dump_file->write($insert->get_sql() . $eol);
354 }
355 $this->dump_file->write($eol . $eol);
356 }
357
358 public function get_create_table_sql($table) {
359 $create_table_sql = $this->db->fetch('SHOW CREATE TABLE `' . $table . '`');
360 return $create_table_sql[0]['Create Table'] . ';';
361 }
362 }
363
364 class Shuttle_DBConn {
365 public $host;
366 public $username;
367 public $password;
368 public $name;
369
370 protected $connection;
371
372 function __construct($options) {
373 $this->host = $options['host'];
374 if (empty($this->host)) {
375 $this->host = '127.0.0.1';
376 }
377 $this->username = $options['username'];
378 $this->password = $options['password'];
379 $this->name = $options['db_name'];
380 }
381
382 static function create($options) {
383 if (class_exists('mysqli')) {
384 $class_name = "Shuttle_DBConn_Mysqli";
385 } else {
386 $class_name = "Shuttle_DBConn_Mysql";
387 }
388
389 return new $class_name($options);
390 }
391 }
392
393 class Shuttle_DBConn_Mysql extends Shuttle_DBConn {
394 function connect() {
395 $this->connection = @mysql_connect($this->host, $this->username, $this->password);
396 if (!$this->connection) {
397 throw new Shuttle_Exception("Couldn't connect to the database: " . mysql_error());
398 }
399
400 $select_db_res = mysql_select_db($this->name, $this->connection);
401 if (!$select_db_res) {
402 throw new Shuttle_Exception("Couldn't select database: " . mysql_error($this->connection));
403 }
404
405 return true;
406 }
407
408 function query($q) {
409 if (!$this->connection) {
410 $this->connect();
411 }
412 $res = mysql_query($q);
413 if (!$res) {
414 throw new Shuttle_Exception("SQL error: " . mysql_error($this->connection));
415 }
416 return $res;
417 }
418
419 function fetch_numeric($query) {
420 return $this->fetch($query, MYSQL_NUM);
421 }
422
423 function fetch($query, $result_type=MYSQL_ASSOC) {
424 $result = $this->query($query, $this->connection);
425 $return = array();
426 while ( $row = mysql_fetch_array($result, $result_type) ) {
427 $return[] = $row;
428 }
429 return $return;
430 }
431
432 function escape($value) {
433 if (is_null($value)) {
434 return "NULL";
435 }
436 return "'" . mysql_real_escape_string($value) . "'";
437 }
438
439 function escape_like($search) {
440 return str_replace(array('_', '%'), array('\_', '\%'), $search);
441 }
442
443 function get_var($sql) {
444 $result = $this->query($sql);
445 $row = mysql_fetch_array($result);
446 return $row[0];
447 }
448
449 function fetch_row($data) {
450 return mysql_fetch_assoc($data);
451 }
452 }
453
454
455 class Shuttle_DBConn_Mysqli extends Shuttle_DBConn {
456 function connect() {
457 $this->connection = @new MySQLi($this->host, $this->username, $this->password, $this->name);
458
459 if ($this->connection->connect_error) {
460 throw new Shuttle_Exception("Couldn't connect to the database: " . $this->connection->connect_error);
461 }
462
463 return true;
464 }
465
466 function query($q) {
467 if (!$this->connection) {
468 $this->connect();
469 }
470 $res = $this->connection->query($q);
471
472 if (!$res) {
473 throw new Shuttle_Exception("SQL error: " . $this->connection->error);
474 }
475
476 return $res;
477 }
478
479 function fetch_numeric($query) {
480 return $this->fetch($query, MYSQLI_NUM);
481 }
482
483 function fetch($query, $result_type=MYSQLI_ASSOC) {
484 $result = $this->query($query, $this->connection);
485 $return = array();
486 while ( $row = $result->fetch_array($result_type) ) {
487 $return[] = $row;
488 }
489 return $return;
490 }
491
492 function escape($value) {
493 if (is_null($value)) {
494 return "NULL";
495 }
496 return "'" . $this->connection->real_escape_string($value) . "'";
497 }
498
499 function escape_like($search) {
500 return str_replace(array('_', '%'), array('\_', '\%'), $search);
501 }
502
503 function get_var($sql) {
504 $result = $this->query($sql);
505 $row = $result->fetch_array($result, MYSQLI_NUM);
506 return $row[0];
507 }
508
509 function fetch_row($data) {
510 return $data->fetch_array(MYSQLI_ASSOC);
511 }
512 }
513
514 class Shuttle_Exception extends Exception {};
515