3 * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
\r
4 * Copyright (C) 2002-2012 The Nucleus Group
\r
6 * This program is free software; you can redistribute it and/or
\r
7 * modify it under the terms of the GNU General Public License
\r
8 * as published by the Free Software Foundation; either version 2
\r
9 * of the License, or (at your option) any later version.
\r
10 * (see nucleus/documentation/index.html#license for more info)
\r
13 * Scripts to create/restore a backup of the Nucleus database
\r
15 * @license http://nucleuscms.org/license.txt GNU General Public License
\r
16 * @copyright Copyright (C) 2002-2012 The Nucleus Group
\r
17 * @version $Id: backup.php 1624 2012-01-09 11:36:20Z sakamocchi $
\r
24 * Constructor, just for compatibility
\r
31 public function Backup()
\r
37 * Backup::do_backup()
\r
38 * This function creates an sql dump of the database and sends it to
\r
39 * the user as a file (can be gzipped if they want)
\r
41 * NOTE: this remains not-static for compatibility
\r
43 * @param boolean $gzip 1 = compress backup file, 0 = no compression (default)
\r
47 public function do_backup($gzip = 0)
\r
49 global $manager, $nucleus;
\r
51 // tables of which backup is needed
\r
53 sql_table('actionlog'),
\r
56 sql_table('comment'),
\r
57 sql_table('config'),
\r
60 sql_table('member'),
\r
62 sql_table('skin_desc'),
\r
64 sql_table('template'),
\r
65 sql_table('template_desc'),
\r
66 sql_table('plugin'),
\r
67 sql_table('plugin_event'),
\r
68 sql_table('plugin_option'),
\r
69 sql_table('plugin_option_desc'),
\r
70 sql_table('category'),
\r
71 sql_table('activation'),
\r
72 sql_table('tickets'),
\r
75 // add tables that plugins want to backup to the list
\r
76 // catch all output generated by plugins
\r
78 $query = sprintf('SELECT pfile FROM %s', sql_table('plugin'));
\r
79 $res = DB::getResult($query);
\r
80 foreach ( $res as $row )
\r
82 $plug =& $manager->getPlugin($row['pfile']);
\r
85 $tables = array_merge($tables, (array) $plug->getTableList());
\r
90 // remove duplicates
\r
91 $tables = array_unique($tables);
\r
93 // make sure browsers don't cache the backup
\r
94 header("Pragma: no-cache");
\r
96 // don't allow gzip compression when extension is not loaded
\r
97 if ( ($gzip != 0) && !extension_loaded("zlib") )
\r
104 $filename = 'nucleus_db_backup_' . i18n::formatted_datetime('%Y-%m-%d-%H-%M-%S', time()) . ".sql";
\r
108 // use an output buffer
\r
110 @ob_implicit_flush(0);
\r
113 $filename = 'nucleus_db_backup_' . i18n::formatted_datetime('%Y-%m-%d-%H-%M-%S', time()) . ".sql.gz";
\r
116 // send headers that tell the browser a file is coming
\r
117 header("Content-Type: text/x-delimtext; name=\"$filename\"");
\r
118 header("Content-disposition: attachment; filename=$filename");
\r
122 echo " * This is a backup file generated by Nucleus \n";
\r
123 echo " * http://www.nucleuscms.org/\n";
\r
125 echo " * backup-date: " . i18n::formatted_datetime('rfc822GMT', time()) . "\n";
\r
126 echo " * Nucleus CMS version: " . $nucleus['version'] . "\n";
\r
128 echo " * WARNING: Only try to restore on servers running the exact same version of Nucleus\n";
\r
133 array_walk($tables, array(__CLASS__, 'dump_table'));
\r
137 $Size = ob_get_length();
\r
138 $Crc = crc32(ob_get_contents());
\r
139 $contents = gzcompress(ob_get_contents());
\r
141 echo "\x1f\x8b\x08\x00\x00\x00\x00\x00" . substr($contents, 0, strlen($contents) - 4)
\r
142 . self::gzip_print_four_characters($Crc) . self::gzip_print_four_characters($Size);
\r
148 * Backup::dump_table()
\r
149 * Creates a dump for a single table
\r
150 * ($tablename and $key are filled in by array_walk)
\r
153 * @param string $tablename
\r
154 * @param string $key
\r
156 static private function dump_table($tablename, $key)
\r
159 echo " * TABLE: " . $tablename . "\n";
\r
162 // dump table structure
\r
163 self::dump_structure($tablename);
\r
165 // dump table contents
\r
166 self::dump_contents($tablename);
\r
171 * Backup::dump_structure()
\r
172 * Creates a dump of the table structure for one table
\r
175 * @param string $tablename
\r
179 static private function dump_structure($tablename)
\r
181 // add command to drop table on restore
\r
182 echo "DROP TABLE IF EXISTS {$tablename};\n\n";
\r
183 $result = DB::getRow("SHOW CREATE TABLE {$tablename}");
\r
184 echo $result['Create Table'];
\r
190 * Backup::get_field_names()
\r
191 * Returns the field named for the given table in the
\r
192 * following format:
\r
193 * (column1, column2, ..., columnn)
\r
196 * @param resource $result
\r
197 * @param integer $num_fields
\r
200 static private function get_field_names($result, $num_fields)
\r
203 for ( $j = 0; $j < $num_fields; $j++ )
\r
205 $col = $result->getColumnMeta($j);
\r
206 $fields[] = $col['name'];
\r
209 return '(' . implode(', ', $fields) . ')';
\r
213 * Backup::dump_contents()
\r
214 * Creates a dump of the table content for one table
\r
217 * @param string $tablename
\r
221 static private function dump_contents($tablename)
\r
224 * Grab the data from the table.
\r
226 $result = DB::getResult("SELECT * FROM $tablename");
\r
228 if ( $result->rowCount() > 0 )
\r
232 echo " * Table Data for {$tablename}\n";
\r
236 $num_fields = $result->columnCount();
\r
239 * Compose fieldname list
\r
241 $tablename_list = self::get_field_names($result, $num_fields);
\r
244 * Loop through the resulting rows and build the sql statement.
\r
246 foreach ( $result as $row )
\r
248 // Start building the SQL statement.
\r
249 echo 'INSERT INTO ' . $tablename . ' ' . $tablename_list . ' VALUES(';
\r
251 // Loop through the rows and fill in data for each column
\r
252 for ( $j = 0; $j < $num_fields; $j++ )
\r
254 if ( !isset($row[$j]) )
\r
256 // no data for column
\r
259 elseif ( $row[$j] != '' )
\r
262 echo ' ' . DB::quoteValue($row[$j]);
\r
266 // empty column (!= no data!)
\r
270 // only add comma when not last column
\r
271 if ( $j != ($num_fields - 1) )
\r
283 * Backup::gzip_print_four_characters()
\r
286 * @param integer $val
\r
289 static private function gzip_print_four_characters($Val)
\r
291 for ( $i = 0; $i < 4; $i ++ )
\r
293 $return .= chr($Val % 256);
\r
294 $Val = floor($Val / 256);
\r
300 * Backup::do_restore()
\r
301 * Restores a database backup
\r
303 * NOTE: this remains not-static for compatibility
\r
308 public function do_restore()
\r
310 $uploadInfo = postFileInfo('backup_file');
\r
312 // first of all: get uploaded file:
\r
313 if ( array_key_exists('name', $uploadInfo) && empty($uploadInfo['name']) )
\r
315 return 'No file uploaded';
\r
317 if ( !is_uploaded_file($uploadInfo['tmp_name']) )
\r
319 return 'No file uploaded';
\r
322 $backup_file_name = $uploadInfo['name'];
\r
323 $backup_file_tmpname = $uploadInfo['tmp_name'];
\r
324 $backup_file_type = $uploadInfo['type'];
\r
326 if ( !file_exists($backup_file_tmpname) )
\r
328 return 'File Upload Error';
\r
331 if ( !preg_match("#^(text/[a-zA-Z]+)|(application/(x\-)?gzip(\-compressed)?)|(application/octet-stream)$#i", $backup_file_type) )
\r
333 return 'The uploaded file is not of the correct type';
\r
337 if ( preg_match("#\.gz#i", $backup_file_name) )
\r
342 if ( !extension_loaded("zlib") && $gzip )
\r
344 return 'Cannot decompress gzipped backup (zlib package not installed)';
\r
347 // get sql query according to gzip setting (either decompress, or not)
\r
348 $contents = self::get_contents($backup_file_tmpname, $gzip);
\r
349 if ( $contents == '' )
\r
351 return 'Cannot get contents from this file.';
\r
355 $lines = preg_split('/[\r\n]/', $contents);
\r
356 if( $lines === $contents )
\r
358 return 'Cannot parse contents from this file';
\r
361 /* get sql statements from each lines */
\r
362 $queries = self::get_queries($lines);
\r
363 if ( $queries === array() )
\r
365 return "Cannot get SQL queries from this file.";
\r
368 /* execute sql statements */
\r
369 foreach ( $queries as $query )
\r
371 if ( DB::execute($query) === FALSE )
\r
373 $error = DB::getError();
\r
374 debug('SQL Error: ' . $error[2]);
\r
382 static private function get_contents($temporary_name, $gzip = 0)
\r
387 // decompress and read
\r
388 $gz_ptr = gzopen($temporary_name, 'rb');
\r
389 while ( !gzeof($gz_ptr) )
\r
391 $contents .= gzgets($gz_ptr, 100000);
\r
397 $fsize = filesize($temporary_name);
\r
400 $contents = fread(fopen($temporary_name, 'r'), $fsize);
\r
406 static private function get_queries($lines)
\r
409 $queries = array();
\r
410 foreach ( $lines as $line )
\r
412 $line = trim($line);
\r
413 if ( !$line || $line[0] == '#' || preg_match('#^[\s|/]?\*#', $line) )
\r
418 if ( preg_match('/^(.*);$/', $line, $matches) === 0 )
\r
424 $query .= $matches[1];
\r
425 $queries[] = $query;
\r