OSDN Git Service

FIX:変数名の誤記を修正
[nucleus-jp/nucleus-next.git] / nucleus / libs / backup.php
1 <?php\r
2 /*\r
3  * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)\r
4  * Copyright (C) 2002-2012 The Nucleus Group\r
5  *\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
11  */\r
12 /**\r
13  * Scripts to create/restore a backup of the Nucleus database\r
14  *\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
18  */\r
19 \r
20 class Backup\r
21 {\r
22         /**\r
23          * Backup::Backup()\r
24          * Constructor, just for compatibility\r
25          *\r
26          * @deprecated\r
27          * @param       void\r
28          * @return      void\r
29          *\r
30          */\r
31         public function Backup()\r
32         {\r
33                 return;\r
34         }\r
35 \r
36         /**\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
40          *\r
41          * NOTE: this remains not-static for compatibility\r
42          *\r
43          * @param boolean       $gzip   1 = compress backup file, 0 = no compression (default)\r
44          * @return      void\r
45          *\r
46          */\r
47         public function do_backup($gzip = 0)\r
48         {\r
49                 global $manager, $nucleus;\r
50 \r
51                 // tables of which backup is needed\r
52                 $tables = array(\r
53                                 sql_table('actionlog'),\r
54                                 sql_table('ban'),\r
55                                 sql_table('blog'),\r
56                                 sql_table('comment'),\r
57                                 sql_table('config'),\r
58                                 sql_table('item'),\r
59                                 sql_table('karma'),\r
60                                 sql_table('member'),\r
61                                 sql_table('skin'),\r
62                                 sql_table('skin_desc'),\r
63                                 sql_table('team'),\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
73                 );\r
74 \r
75                 // add tables that plugins want to backup to the list\r
76                 // catch all output generated by plugins\r
77                 ob_start();\r
78                 $query = sprintf('SELECT pfile FROM %s', sql_table('plugin'));\r
79                 $res = DB::getResult($query);\r
80                 foreach ( $res as $row )\r
81                 {\r
82                         $plug =& $manager->getPlugin($row['pfile']);\r
83                         if ( $plug )\r
84                         {\r
85                                 $tables = array_merge($tables, (array) $plug->getTableList());\r
86                         }\r
87                 }\r
88                 ob_end_clean();\r
89 \r
90                 // remove duplicates\r
91                 $tables = array_unique($tables);\r
92 \r
93                 // make sure browsers don't cache the backup\r
94                 header("Pragma: no-cache");\r
95 \r
96                 // don't allow gzip compression when extension is not loaded\r
97                 if ( ($gzip != 0) && !extension_loaded("zlib") )\r
98                 {\r
99                         $gzip = 0;\r
100                 }\r
101 \r
102                 if ( !$gzip )\r
103                 {\r
104                         $filename = 'nucleus_db_backup_' . i18n::formatted_datetime('%Y-%m-%d-%H-%M-%S', time()) . ".sql";\r
105                 }\r
106                 else\r
107                 {\r
108                         // use an output buffer\r
109                         @ob_start();\r
110                         @ob_implicit_flush(0);\r
111 \r
112                         // set filename\r
113                         $filename = 'nucleus_db_backup_' . i18n::formatted_datetime('%Y-%m-%d-%H-%M-%S', time()) . ".sql.gz";\r
114                 }\r
115 \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
119 \r
120                 // dump header\r
121                 echo "/*\n";\r
122                 echo " * This is a backup file generated by Nucleus \n";\r
123                 echo " * http://www.nucleuscms.org/\n";\r
124                 echo " * \n";\r
125                 echo " * backup-date: " . i18n::formatted_datetime('rfc822GMT', time()) . "\n";\r
126                 echo " * Nucleus CMS version: " . $nucleus['version'] . "\n";\r
127                 echo " * \n";\r
128                 echo " * WARNING: Only try to restore on servers running the exact same version of Nucleus\n";\r
129                 echo " */\n";\r
130 \r
131                 // dump all tables\r
132                 reset($tables);\r
133                 array_walk($tables, array(__CLASS__, 'dump_table'));\r
134 \r
135                 if ( $gzip )\r
136                 {\r
137                         $Size = ob_get_length();\r
138                         $Crc = crc32(ob_get_contents());\r
139                         $contents = gzcompress(ob_get_contents());\r
140                         ob_end_clean();\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
143                 }\r
144                 exit;\r
145         }\r
146 \r
147         /**\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
151          *\r
152          * @static\r
153          * @param       string  $tablename\r
154          * @param       string  $key\r
155          */\r
156         static private function dump_table($tablename, $key)\r
157         {\r
158                 echo "/*\n";\r
159                 echo " * TABLE: " . $tablename . "\n";\r
160                 echo " */\n";\r
161 \r
162                 // dump table structure\r
163                 self::dump_structure($tablename);\r
164 \r
165                 // dump table contents\r
166                 self::dump_contents($tablename);\r
167                 return;\r
168         }\r
169 \r
170         /**\r
171          * Backup::dump_structure()\r
172          * Creates a dump of the table structure for one table\r
173          *\r
174          * @static\r
175          * @param       string  $tablename\r
176          * @return      void\r
177          *\r
178          */\r
179         static private function dump_structure($tablename)\r
180         {\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
185                 echo ";\n\n";\r
186                 return;\r
187         }\r
188 \r
189         /**\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
194          *\r
195          * @static\r
196          * @param       resource        $result\r
197          * @param       integer $num_fields\r
198          * @return      string\r
199          */\r
200         static private function get_field_names($result, $num_fields)\r
201         {\r
202                 $fields = array();\r
203                 for ( $j = 0; $j < $num_fields; $j++ )\r
204                 {\r
205                         $col = $result->getColumnMeta($j);\r
206                         $fields[] = $col['name'];\r
207                 }\r
208 \r
209                 return '(' . implode(', ', $fields) . ')';\r
210         }\r
211 \r
212         /**\r
213          * Backup::dump_contents()\r
214          * Creates a dump of the table content for one table\r
215          *\r
216          * @static\r
217          * @param       string  $tablename\r
218          * @return      void\r
219          *\r
220          */\r
221         static private function dump_contents($tablename)\r
222         {\r
223                 /*\r
224                  * Grab the data from the table.\r
225                 */\r
226                 $result = DB::getResult("SELECT * FROM $tablename");\r
227 \r
228                 if ( $result->rowCount() > 0 )\r
229                 {\r
230                         echo "\n";\r
231                         echo "/*\n";\r
232                         echo " * Table Data for {$tablename}\n";\r
233                         echo " */\n";\r
234                 }\r
235 \r
236                 $num_fields = $result->columnCount();\r
237 \r
238                 /*\r
239                  * Compose fieldname list\r
240                 */\r
241                 $tablename_list = self::get_field_names($result, $num_fields);\r
242 \r
243                 /*\r
244                  * Loop through the resulting rows and build the sql statement.\r
245                 */\r
246                 foreach ( $result as $row )\r
247                 {\r
248                         // Start building the SQL statement.\r
249                         echo 'INSERT INTO ' . $tablename . ' ' . $tablename_list . ' VALUES(';\r
250 \r
251                         // Loop through the rows and fill in data for each column\r
252                         for ( $j = 0; $j < $num_fields; $j++ )\r
253                         {\r
254                                 if ( !isset($row[$j]) )\r
255                                 {\r
256                                         // no data for column\r
257                                         echo ' NULL';\r
258                                 }\r
259                                 elseif ( $row[$j] != '' )\r
260                                 {\r
261                                         // data\r
262                                         echo ' ' . DB::quoteValue($row[$j]);\r
263                                 }\r
264                                 else\r
265                                 {\r
266                                         // empty column (!= no data!)\r
267                                         echo "''";\r
268                                 }\r
269 \r
270                                 // only add comma when not last column\r
271                                 if ( $j != ($num_fields - 1) )\r
272                                 {\r
273                                         echo ',';\r
274                                 }\r
275                         }\r
276                         echo ");\n";\r
277                 }\r
278                 echo "\n";\r
279                 return;\r
280         }\r
281 \r
282         /**\r
283          * Backup::gzip_print_four_characters()\r
284          *\r
285          * @static\r
286          * @param       integer $val\r
287          * @return      integer\r
288          */\r
289         static private function gzip_print_four_characters($Val)\r
290         {\r
291                 for ( $i = 0; $i < 4; $i ++ )\r
292                 {\r
293                         $return .= chr($Val % 256);\r
294                         $Val = floor($Val / 256);\r
295                 }\r
296                 return $return;\r
297         }\r
298 \r
299         /**\r
300          * Backup::do_restore()\r
301          * Restores a database backup\r
302          *\r
303          * NOTE: this remains not-static for compatibility\r
304          *\r
305          * @param       void\r
306          * @return      void\r
307          */\r
308         public function do_restore()\r
309         {\r
310                 $uploadInfo = postFileInfo('backup_file');\r
311 \r
312                 // first of all: get uploaded file:\r
313                 if ( array_key_exists('name', $uploadInfo) && empty($uploadInfo['name']) )\r
314                 {\r
315                         return 'No file uploaded';\r
316                 }\r
317                 if ( !is_uploaded_file($uploadInfo['tmp_name']) )\r
318                 {\r
319                         return 'No file uploaded';\r
320                 }\r
321 \r
322                 $backup_file_name = $uploadInfo['name'];\r
323                 $backup_file_tmpname = $uploadInfo['tmp_name'];\r
324                 $backup_file_type = $uploadInfo['type'];\r
325 \r
326                 if ( !file_exists($backup_file_tmpname) )\r
327                 {\r
328                         return 'File Upload Error';\r
329                 }\r
330 \r
331                 if ( !preg_match("#^(text/[a-zA-Z]+)|(application/(x\-)?gzip(\-compressed)?)|(application/octet-stream)$#i", $backup_file_type) )\r
332                 {\r
333                         return 'The uploaded file is not of the correct type';\r
334                 }\r
335 \r
336                 $gzip = 0;\r
337                 if ( preg_match("#\.gz#i", $backup_file_name) )\r
338                 {\r
339                         $gzip = 1;\r
340                 }\r
341 \r
342                 if ( !extension_loaded("zlib") && $gzip )\r
343                 {\r
344                         return 'Cannot decompress gzipped backup (zlib package not installed)';\r
345                 }\r
346 \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
350                 {\r
351                         return 'Cannot get contents from this file.';\r
352                 }\r
353 \r
354                 /* detect lines */\r
355                 $lines = preg_split('/[\r\n]/', $contents);\r
356                 if( $lines === $contents )\r
357                 {\r
358                         return 'Cannot parse contents from this file';\r
359                 }\r
360 \r
361                 /* get sql statements from each lines */\r
362                 $queries = self::get_queries($lines);\r
363                 if ( $queries === array() )\r
364                 {\r
365                         return "Cannot get SQL queries from this file.";\r
366                 }\r
367 \r
368                 /* execute sql statements */\r
369                 foreach ( $queries as $query )\r
370                 {\r
371                         if ( DB::execute($query) === FALSE )\r
372                         {\r
373                                 $error = DB::getError();\r
374                                 debug('SQL Error: ' . $error[2]);\r
375                                 break;\r
376                         }\r
377                         continue;\r
378                 }\r
379                 return;\r
380         }\r
381 \r
382         static private function get_contents($temporary_name, $gzip = 0)\r
383         {\r
384                 $contents = '';\r
385                 if ( $gzip )\r
386                 {\r
387                         // decompress and read\r
388                         $gz_ptr = gzopen($temporary_name, 'rb');\r
389                         while ( !gzeof($gz_ptr) )\r
390                         {\r
391                                 $contents .= gzgets($gz_ptr, 100000);\r
392                         }\r
393                 }\r
394                 else\r
395                 {\r
396                         // just read\r
397                         $fsize = filesize($temporary_name);\r
398                         if ( $fsize > 0 )\r
399                         {\r
400                                 $contents = fread(fopen($temporary_name, 'r'), $fsize);\r
401                         }\r
402                 }\r
403                 return $contents;\r
404         }\r
405 \r
406         static private function get_queries($lines)\r
407         {\r
408                 $query = '';\r
409                 $queries = array();\r
410                 foreach ( $lines as $line )\r
411                 {\r
412                         $line = trim($line);\r
413                         if ( !$line || $line[0] == '#' || preg_match('#^[\s|/]?\*#', $line) )\r
414                         {\r
415                                 continue;\r
416                         }\r
417 \r
418                         if ( preg_match('/^(.*);$/', $line, $matches) === 0 )\r
419                         {\r
420                                 $query .= $line;\r
421                         }\r
422                         else\r
423                         {\r
424                                 $query .= $matches[1];\r
425                                 $queries[] = $query;\r
426                                 $query = '';\r
427                         }\r
428                         continue;\r
429                 }\r
430                 return $queries;\r
431         }\r
432 }