3 * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
4 * Copyright (C) 2002-2009 The Nucleus Group
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 * (see nucleus/documentation/index.html#license for more info)
13 * Scripts to create/restore a backup of the Nucleus database
15 * @license http://nucleuscms.org/license.txt GNU General Public License
16 * @copyright Copyright (C) 2002-2009 The Nucleus Group
17 * @version $Id: backup.php 1812 2012-05-01 14:59:07Z sakamocchi $
24 * Constructor, just for compatibility
31 public function Backup()
38 * This function creates an sql dump of the database and sends it to
39 * the user as a file (can be gzipped if they want)
41 * NOTE: this remains not-static for compatibility
43 * @param boolean $gzip 1 = compress backup file, 0 = no compression (default)
47 public function do_backup($gzip = 0)
49 global $manager, $nucleus;
51 // tables of which backup is needed
53 sql_table('actionlog'),
62 sql_table('skin_desc'),
64 sql_table('template'),
65 sql_table('template_desc'),
67 sql_table('plugin_event'),
68 sql_table('plugin_option'),
69 sql_table('plugin_option_desc'),
70 sql_table('category'),
71 sql_table('activation'),
75 // add tables that plugins want to backup to the list
76 // catch all output generated by plugins
78 $query = sprintf('SELECT pfile FROM %s', sql_table('plugin'));
79 $res = DB::getResult($query);
80 foreach ( $res as $row )
82 $plug =& $manager->getPlugin($row['pfile']);
85 $tables = array_merge($tables, (array) $plug->getTableList());
91 $tables = array_unique($tables);
93 // make sure browsers don't cache the backup
94 header("Pragma: no-cache");
96 // don't allow gzip compression when extension is not loaded
97 if ( ($gzip != 0) && !extension_loaded("zlib") )
104 $filename = 'nucleus_db_backup_' . i18n::formatted_datetime('%Y-%m-%d-%H-%M-%S', time()) . ".sql";
108 // use an output buffer
110 @ob_implicit_flush(0);
113 $filename = 'nucleus_db_backup_' . i18n::formatted_datetime('%Y-%m-%d-%H-%M-%S', time()) . ".sql.gz";
116 // send headers that tell the browser a file is coming
117 header("Content-Type: text/x-delimtext; name=\"$filename\"");
118 header("Content-disposition: attachment; filename=$filename");
122 echo " * This is a backup file generated by Nucleus \n";
123 echo " * http://www.nucleuscms.org/\n";
125 echo " * backup-date: " . i18n::formatted_datetime('rfc822GMT', time()) . "\n";
126 echo " * Nucleus CMS version: " . $nucleus['version'] . "\n";
128 echo " * WARNING: Only try to restore on servers running the exact same version of Nucleus\n";
133 /* NOTE: hope to use 'self' keyword here but works bad so here use __CLASS__ macro. */
134 array_walk($tables, array(__CLASS__, 'dump_table'));
138 $Size = ob_get_length();
139 $Crc = crc32(ob_get_contents());
140 $contents = gzcompress(ob_get_contents());
142 echo "\x1f\x8b\x08\x00\x00\x00\x00\x00" . substr($contents, 0, strlen($contents) - 4)
143 . self::gzip_print_four_characters($Crc) . self::gzip_print_four_characters($Size);
149 * Backup::dump_table()
150 * Creates a dump for a single table
151 * ($tablename and $key are filled in by array_walk)
154 * @param string $tablename
157 static private function dump_table($tablename, $key)
160 echo " * TABLE: " . $tablename . "\n";
163 // dump table structure
164 self::dump_structure($tablename);
166 // dump table contents
167 self::dump_contents($tablename);
172 * Backup::dump_structure()
173 * Creates a dump of the table structure for one table
176 * @param string $tablename
180 static private function dump_structure($tablename)
182 // add command to drop table on restore
183 echo "DROP TABLE IF EXISTS {$tablename};\n\n";
184 $result = DB::getRow("SHOW CREATE TABLE {$tablename}");
185 echo $result['Create Table'];
191 * Backup::get_field_names()
192 * Returns the field named for the given table in the
194 * (column1, column2, ..., columnn)
197 * @param resource $result
198 * @param integer $num_fields
201 static private function get_field_names($result, $num_fields)
204 for ( $j = 0; $j < $num_fields; $j++ )
206 $col = $result->getColumnMeta($j);
207 $fields[] = $col['name'];
210 return '(' . implode(', ', $fields) . ')';
214 * Backup::dump_contents()
215 * Creates a dump of the table content for one table
218 * @param string $tablename
222 static private function dump_contents($tablename)
225 * Grab the data from the table.
227 $result = DB::getResult("SELECT * FROM $tablename");
229 if ( $result->rowCount() > 0 )
233 echo " * Table Data for {$tablename}\n";
237 $num_fields = $result->columnCount();
240 * Compose fieldname list
242 $tablename_list = self::get_field_names($result, $num_fields);
245 * Loop through the resulting rows and build the sql statement.
247 foreach ( $result as $row )
249 // Start building the SQL statement.
250 echo 'INSERT INTO ' . $tablename . ' ' . $tablename_list . ' VALUES(';
252 // Loop through the rows and fill in data for each column
253 for ( $j = 0; $j < $num_fields; $j++ )
255 if ( !isset($row[$j]) )
257 // no data for column
260 elseif ( $row[$j] != '' )
263 echo ' ' . DB::quoteValue($row[$j]);
267 // empty column (!= no data!)
271 // only add comma when not last column
272 if ( $j != ($num_fields - 1) )
284 * Backup::gzip_print_four_characters()
287 * @param integer $val
290 static private function gzip_print_four_characters($Val)
292 for ( $i = 0; $i < 4; $i ++ )
294 $return .= chr($Val % 256);
295 $Val = floor($Val / 256);
301 * Backup::do_restore()
302 * Restores a database backup
304 * NOTE: this remains not-static for compatibility
309 public function do_restore()
311 $uploadInfo = postFileInfo('backup_file');
313 // first of all: get uploaded file:
314 if ( array_key_exists('name', $uploadInfo) && empty($uploadInfo['name']) )
316 return 'No file uploaded';
318 if ( !is_uploaded_file($uploadInfo['tmp_name']) )
320 return 'No file uploaded';
323 $backup_file_name = $uploadInfo['name'];
324 $backup_file_tmpname = $uploadInfo['tmp_name'];
325 $backup_file_type = $uploadInfo['type'];
327 if ( !file_exists($backup_file_tmpname) )
329 return 'File Upload Error';
332 if ( !preg_match("#^(text/[a-zA-Z]+)|(application/(x\-)?gzip(\-compressed)?)|(application/octet-stream)$#i", $backup_file_type) )
334 return 'The uploaded file is not of the correct type';
338 if ( preg_match("#\.gz#i", $backup_file_name) )
343 if ( !extension_loaded("zlib") && $gzip )
345 return 'Cannot decompress gzipped backup (zlib package not installed)';
348 // get sql query according to gzip setting (either decompress, or not)
349 $contents = self::get_contents($backup_file_tmpname, $gzip);
350 if ( $contents == '' )
352 return 'Cannot get contents from this file.';
356 $lines = preg_split('/[\r\n]/', $contents);
357 if( $lines === $contents )
359 return 'Cannot parse contents from this file';
362 /* get sql statements from each lines */
363 $queries = self::get_queries($lines);
364 if ( $queries === array() )
366 return "Cannot get SQL queries from this file.";
369 /* execute sql statements */
370 foreach ( $queries as $query )
372 if ( DB::execute($query) === FALSE )
374 $error = DB::getError();
375 debug('SQL Error: ' . $error[2]);
383 static private function get_contents($temporary_name, $gzip = 0)
388 // decompress and read
389 $gz_ptr = gzopen($temporary_name, 'rb');
390 while ( !gzeof($gz_ptr) )
392 $contents .= gzgets($gz_ptr, 100000);
398 $fsize = filesize($temporary_name);
401 $contents = fread(fopen($temporary_name, 'r'), $fsize);
407 static private function get_queries($lines)
411 foreach ( $lines as $line )
414 if ( !$line || $line[0] == '#' || preg_match('#^[\s|/]?\*#', $line) )
419 if ( preg_match('/^(.*);$/', $line, $matches) === 0 )
425 $query .= $matches[1];