一个不错的MYSQL数据库备份类,PHP版,一个文件,精简版

  1 <?php
  2 class DbManage {
  3     var $db; // 数据库连接
  4     var $database; // 所用数据库
  5     var $sqldir; // 数据库备份文件夹
  6     // 换行符
  7     private $ds = "\n";
  8     // 存储SQL的变量
  9     public $sqlContent = "";
 10     // 每条sql语句的结尾符
 11     public $sqlEnd = ";";
 12 
 13     /**
 14      * 初始化
 15      *
 16      * @param string $host
 17      * @param string $username
 18      * @param string $password
 19      * @param string $database
 20      * @param string $charset
 21      */
 22     function __construct($host = 'localhost', $username = 'root', $password = '', $database = 'test', $charset = 'utf8') {
 23         $this->host = $host;
 24         $this->username = $username;
 25         $this->password = $password;
 26         $this->database = $database;
 27         $this->charset = $charset;
 28         set_time_limit(0);//无时间限制
 29         @ob_end_flush();
 30         // 连接数据库
 31         $this->db = @mysql_connect ( $this->host, $this->username, $this->password ) or die( '<p class="dbDebug"><span class="err">Mysql Connect Error : </span>'.mysql_error().'</p>');
 32         // 选择使用哪个数据库
 33         mysql_select_db ( $this->database, $this->db ) or die('<p class="dbDebug"><span class="err">Mysql Connect Error:</span>'.mysql_error().'</p>');
 34         // 数据库编码方式
 35         mysql_query ( 'SET NAMES ' . $this->charset, $this->db );
 36 
 37     }
 38 
 39     /*
 40      * 新增查询数据库表
 41      */
 42     function getTables() {
 43         $res = mysql_query ( "SHOW TABLES" );
 44         $tables = array ();
 45         while ( $row = mysql_fetch_array ( $res ) ) {
 46             $tables [] = $row [0];
 47         }
 48         return $tables;
 49     }
 50 
 51     /*
 52      *
 53      * ------------------------------------------数据库备份start----------------------------------------------------------
 54      */
 55 
 56     /**
 57      * 数据库备份
 58      * 参数:备份哪个表(可选),备份目录(可选,默认为backup),分卷大小(可选,默认2000,即2M)
 59      *
 60      * @param $string $dir
 61      * @param int $size
 62      * @param $string $tablename
 63      */
 64     function backup($tablename = '', $dir='./', $size=20480) {
 65         $dir = $dir ? $dir : './';
 66         // 创建目录
 67         if (! is_dir ( $dir )) {
 68             @mkdir ( $dir, 0777, true ) or die ( '创建文件夹失败' );
 69         }
 70         $size = $size ? $size : 1024*20;
 71         $sql = '';
 72         // 只备份某个表
 73         if (! empty ( $tablename )) {
 74             if(@mysql_num_rows(mysql_query("SHOW TABLES LIKE '".$tablename."'")) == 1) {
 75              } else {
 76                 $this->_showMsg('表-<b>' . $tablename .'</b>-不存在,请检查!',true);
 77                 die();
 78             }
 79             $this->_showMsg('正在备份表 <span class="imp">' . $tablename.'</span>');
 80             // 插入dump信息
 81             $sql = $this->_retrieve ();
 82             // 插入表结构信息
 83             $sql .= $this->_insert_table_structure ( $tablename );
 84             // 插入数据
 85             $data = mysql_query ( "select * from " . $tablename );
 86             // 文件名前面部分
 87             $filename = date ( 'YmdHis' ) . "_" . $tablename;
 88             // 字段数量
 89             $num_fields = mysql_num_fields ( $data );
 90             // 第几分卷
 91             $p = 1;
 92             // 循环每条记录
 93             while ( $record = mysql_fetch_array ( $data ) ) {
 94                 // 单条记录
 95                 $sql .= $this->_insert_record ( $tablename, $num_fields, $record );
 96                 // 如果大于分卷大小,则写入文件
 97                 if (strlen ( $sql ) >= $size * 1024) {
 98                     $file = $filename . "_v" . $p . ".sql";
 99                     if ($this->_write_file ( $sql, $file, $dir )) {
100                         $this->_showMsg("表-<b>" . $tablename . "</b>-卷-<b>" . $p . "</b>-数据备份完成,备份文件 [ <span class='imp'>" .$dir . $file ."</span> ]");
101                     } else {
102                         $this->_showMsg("备份表 -<b>" . $tablename . "</b>- 失败",true);
103                         return false;
104                     }
105                     // 下一个分卷
106                     $p ++;
107                     // 重置$sql变量为空,重新计算该变量大小
108                     $sql = "";
109                 }
110             }
111             // 及时清除数据
112             unset($data,$record);
113             // sql大小不够分卷大小
114             if ($sql != "") {
115                 $filename .= "_v" . $p . ".sql";
116                 if ($this->_write_file ( $sql, $filename, $dir )) {
117                     $this->_showMsg( "表-<b>" . $tablename . "</b>-卷-<b>" . $p . "</b>-数据备份完成,备份文件 [ <span class='imp'>" .$dir . $filename ."</span> ]");
118                 } else {
119                     $this->_showMsg("备份卷-<b>" . $p . "</b>-失败<br />");
120                     return false;
121                 }
122             }
123             $this->_showMsg("恭喜您! <span class='imp'>备份成功</span>");
124         } else {
125             $this->_showMsg('正在备份');
126             // 备份全部表
127             if ($tables = mysql_query ( "show table status from " . $this->database )) {
128                 $this->_showMsg("读取数据库结构成功!");
129             } else {
130                 $this->_showMsg("读取数据库结构失败!");
131                 exit ( 0 );
132             }
133             // 插入dump信息
134             $sql .= $this->_retrieve ();
135             // 文件名前面部分
136             $filename = date ( 'YmdHis' ) . "_all";
137             // 查出所有表
138             $tables = mysql_query ( 'SHOW TABLES' );
139             // 第几分卷
140             $p = 1;
141             // 循环所有表
142             while ( $table = mysql_fetch_array ( $tables ) ) {
143                 // 获取表名
144                 $tablename = $table [0];
145                 // 获取表结构
146                 $sql .= $this->_insert_table_structure ( $tablename );
147                 $data = mysql_query ( "select * from " . $tablename );
148                 $num_fields = mysql_num_fields ( $data );
149 
150                 // 循环每条记录
151                 while ( $record = mysql_fetch_array ( $data ) ) {
152                     // 单条记录
153                     $sql .= $this->_insert_record ( $tablename, $num_fields, $record );
154                     // 如果大于分卷大小,则写入文件
155                     if (strlen ( $sql ) >= $size * 1000) {
156 
157                         $file = $filename . "_v" . $p . ".sql";
158                         // 写入文件
159                         if ($this->_write_file ( $sql, $file, $dir )) {
160                             $this->_showMsg("-卷-<b>" . $p . "</b>-数据备份完成,备份文件 [ <span class='imp'>".$dir.$file."</span> ]");
161                         } else {
162                             $this->_showMsg("卷-<b>" . $p . "</b>-备份失败!",true);
163                             return false;
164                         }
165                         // 下一个分卷
166                         $p ++;
167                         // 重置$sql变量为空,重新计算该变量大小
168                         $sql = "";
169                     }
170                 }
171             }
172             // sql大小不够分卷大小
173             if ($sql != "") {
174                 $filename .= "_v" . $p . ".sql";
175                 if ($this->_write_file ( $sql, $filename, $dir )) {
176                     $this->_showMsg("-卷-<b>" . $p . "</b>-数据备份完成,备份文件 [ <span class='imp'>".$dir.$filename."</span> ]");
177                 } else {
178                     $this->_showMsg("卷-<b>" . $p . "</b>-备份失败",true);
179                     return false;
180                 }
181             }
182             $this->_showMsg("恭喜您! <span class='imp'>备份成功</span>");
183         }
184     }
185 
186     //  及时输出信息
187     private function _showMsg($msg,$err=false){
188         $err = $err ? "<span class='err'>ERROR:</span>" : '' ;
189         echo "<p class='dbDebug'>".$err . $msg."</p>";
190         flush();
191 
192     }
193 
194     /**
195      * 插入数据库备份基础信息
196      *
197      * @return string
198      */
199     private function _retrieve() {
200         $value = '';
201         $value .= '--' . $this->ds;
202         $value .= '-- MySQL database dump' . $this->ds;
203         $value .= '-- Created by DbManage class, Power By yanue. ' . $this->ds;
204         $value .= '-- http://yanue.net ' . $this->ds;
205         $value .= '--' . $this->ds;
206         $value .= '-- 主机: ' . $this->host . $this->ds;
207         $value .= '-- 生成日期: ' . date ( 'Y' ) . '' . date ( 'm' ) . '' . date ( 'd' ) . '' . date ( 'H:i' ) . $this->ds;
208         $value .= '-- MySQL版本: ' . mysql_get_server_info () . $this->ds;
209         $value .= '-- PHP 版本: ' . phpversion () . $this->ds;
210         $value .= $this->ds;
211         $value .= '--' . $this->ds;
212         $value .= '-- 数据库: `' . $this->database . '`' . $this->ds;
213         $value .= '--' . $this->ds . $this->ds;
214         $value .= '-- -------------------------------------------------------';
215         $value .= $this->ds . $this->ds;
216         return $value;
217     }
218 
219     /**
220      * 插入表结构
221      *
222      * @param unknown_type $table
223      * @return string
224      */
225     private function _insert_table_structure($table) {
226         $sql = '';
227         $sql .= "--" . $this->ds;
228         $sql .= "-- 表的结构" . $table . $this->ds;
229         $sql .= "--" . $this->ds . $this->ds;
230 
231         // 如果存在则删除表
232         $sql .= "DROP TABLE IF EXISTS `" . $table . '`' . $this->sqlEnd . $this->ds;
233         // 获取详细表信息
234         $res = mysql_query ( 'SHOW CREATE TABLE `' . $table . '`' );
235         $row = mysql_fetch_array ( $res );
236         $sql .= $row [1];
237         $sql .= $this->sqlEnd . $this->ds;
238         // 加上
239         $sql .= $this->ds;
240         $sql .= "--" . $this->ds;
241         $sql .= "-- 转存表中的数据 " . $table . $this->ds;
242         $sql .= "--" . $this->ds;
243         $sql .= $this->ds;
244         return $sql;
245     }
246 
247     /**
248      * 插入单条记录
249      *
250      * @param string $table
251      * @param int $num_fields
252      * @param array $record
253      * @return string
254      */
255     private function _insert_record($table, $num_fields, $record) {
256         // sql字段逗号分割
257         $insert = '';
258         $comma = "";
259         $insert .= "INSERT INTO `" . $table . "` VALUES(";
260         // 循环每个子段下面的内容
261         for($i = 0; $i < $num_fields; $i ++) {
262             $insert .= ($comma . "'" . mysql_escape_string ( $record [$i] ) . "'");
263             $comma = ",";
264         }
265         $insert .= ");" . $this->ds;
266         return $insert;
267     }
268 
269     /**
270      * 写入文件
271      *
272      * @param string $sql
273      * @param string $filename
274      * @param string $dir
275      * @return boolean
276      */
277     private function _write_file($sql, $filename, $dir) {
278         $dir = $dir ? $dir : './backup/';
279         // 创建目录
280         if (! is_dir ( $dir )) {
281             mkdir ( $dir, 0777, true );
282         }
283         $re = true;
284         if (! @$fp = fopen ( $dir . $filename, "w+" )) {
285             $re = false;
286             $this->_showMsg("打开sql文件失败!",true);
287         }
288         if (! @fwrite ( $fp, $sql )) {
289             $re = false;
290             $this->_showMsg("写入sql文件失败,请文件是否可写",true);
291         }
292         if (! @fclose ( $fp )) {
293             $re = false;
294             $this->_showMsg("关闭sql文件失败!",true);
295         }
296         return $re;
297     }
298 
299     /*
300      *
301      * -------------------------------上:数据库导出-----------分割线----------下:数据库导入--------------------------------
302      */
303 
304     /**
305      * 导入备份数据
306      * 说明:分卷文件格式20120516211738_all_v1.sql
307      * 参数:文件路径(必填)
308      *
309      * @param string $sqlfile
310      */
311     function restore($sqlfile) {
312         // 检测文件是否存在
313         if (! file_exists ( $sqlfile )) {
314             $this->_showMsg("sql文件不存在!请检查",true);
315             exit ();
316         }
317         $this->lock ( $this->database );
318         // 获取数据库存储位置
319         $sqlpath = pathinfo ( $sqlfile );
320         $this->sqldir = $sqlpath ['dirname'];
321         // 检测是否包含分卷,将类似20120516211738_all_v1.sql从_v分开,有则说明有分卷
322         $volume = explode ( "_v", $sqlfile );
323         $volume_path = $volume [0];
324         $this->_showMsg("请勿刷新及关闭浏览器以防止程序被中止,如有不慎!将导致数据库结构受损");
325         $this->_showMsg("正在导入备份数据,请稍等!");
326         if (empty ( $volume [1] )) {
327             $this->_showMsg ( "正在导入sql:<span class='imp'>" . $sqlfile . '</span>');
328             // 没有分卷
329             if ($this->_import ( $sqlfile )) {
330                 $this->_showMsg( "数据库导入成功!");
331             } else {
332                  $this->_showMsg('数据库导入失败!',true);
333                 exit ();
334             }
335         } else {
336             // 存在分卷,则获取当前是第几分卷,循环执行余下分卷
337             $volume_id = explode ( ".sq", $volume [1] );
338             // 当前分卷为$volume_id
339             $volume_id = intval ( $volume_id [0] );
340             while ( $volume_id ) {
341                 $tmpfile = $volume_path . "_v" . $volume_id . ".sql";
342                 // 存在其他分卷,继续执行
343                 if (file_exists ( $tmpfile )) {
344                     // 执行导入方法
345                     $this->msg .= "正在导入分卷 $volume_id :<span style='color:#f00;'>" . $tmpfile . '</span><br />';
346                     if ($this->_import ( $tmpfile )) {
347 
348                     } else {
349                         $volume_id = $volume_id ? $volume_id :1;
350                         exit ( "导入分卷:<span style='color:#f00;'>" . $tmpfile . '</span>失败!可能是数据库结构已损坏!请尝试从分卷1开始导入' );
351                     }
352                 } else {
353                     $this->msg .= "此分卷备份全部导入成功!<br />";
354                     return;
355                 }
356                 $volume_id ++;
357             }
358         }if (empty ( $volume [1] )) {
359             $this->_showMsg ( "正在导入sql:<span class='imp'>" . $sqlfile . '</span>');
360             // 没有分卷
361             if ($this->_import ( $sqlfile )) {
362                 $this->_showMsg( "数据库导入成功!");
363             } else {
364                  $this->_showMsg('数据库导入失败!',true);
365                 exit ();
366             }
367         } else {
368             // 存在分卷,则获取当前是第几分卷,循环执行余下分卷
369             $volume_id = explode ( ".sq", $volume [1] );
370             // 当前分卷为$volume_id
371             $volume_id = intval ( $volume_id [0] );
372             while ( $volume_id ) {
373                 $tmpfile = $volume_path . "_v" . $volume_id . ".sql";
374                 // 存在其他分卷,继续执行
375                 if (file_exists ( $tmpfile )) {
376                     // 执行导入方法
377                     $this->msg .= "正在导入分卷 $volume_id :<span style='color:#f00;'>" . $tmpfile . '</span><br />';
378                     if ($this->_import ( $tmpfile )) {
379 
380                     } else {
381                         $volume_id = $volume_id ? $volume_id :1;
382                         exit ( "导入分卷:<span style='color:#f00;'>" . $tmpfile . '</span>失败!可能是数据库结构已损坏!请尝试从分卷1开始导入' );
383                     }
384                 } else {
385                     $this->msg .= "此分卷备份全部导入成功!<br />";
386                     return;
387                 }
388                 $volume_id ++;
389             }
390         }
391     }
392 
393     /**
394      * 将sql导入到数据库(普通导入)
395      *
396      * @param string $sqlfile
397      * @return boolean
398      */
399     private function _import($sqlfile) {
400         // sql文件包含的sql语句数组
401         $sqls = array ();
402         $f = fopen ( $sqlfile, "rb" );
403         // 创建表缓冲变量
404         $create_table = '';
405         while ( ! feof ( $f ) ) {
406             // 读取每一行sql
407             $line = fgets ( $f );
408             // 这一步为了将创建表合成完整的sql语句
409             // 如果结尾没有包含';'(即为一个完整的sql语句,这里是插入语句),并且不包含'ENGINE='(即创建表的最后一句)
410             if (! preg_match ( '/;/', $line ) || preg_match ( '/ENGINE=/', $line )) {
411                 // 将本次sql语句与创建表sql连接存起来
412                 $create_table .= $line;
413                 // 如果包含了创建表的最后一句
414                 if (preg_match ( '/ENGINE=/', $create_table)) {
415                     //执行sql语句创建表
416                     $this->_insert_into($create_table);
417                     // 清空当前,准备下一个表的创建
418                     $create_table = '';
419                 }
420                 // 跳过本次
421                 continue;
422             }
423             //执行sql语句
424             $this->_insert_into($line);
425         }
426         fclose ( $f );
427         return true;
428     }
429 
430     //插入单条sql语句
431     private function _insert_into($sql){
432         if (! mysql_query ( trim ( $sql ) )) {
433             $this->msg .= mysql_error ();
434             return false;
435         }
436     }
437 
438     /*
439      * -------------------------------数据库导入end---------------------------------
440      */
441 
442     // 关闭数据库连接
443     private function close() {
444         mysql_close ( $this->db );
445     }
446 
447     // 锁定数据库,以免备份或导入时出错
448     private function lock($tablename, $op = "WRITE") {
449         if (mysql_query ( "lock tables " . $tablename . " " . $op ))
450             return true;
451         else
452             return false;
453     }
454 
455     // 解锁
456     private function unlock() {
457         if (mysql_query ( "unlock tables" ))
458             return true;
459         else
460             return false;
461     }
462     
463     // 析构
464     function __destruct() {
465         if($this->db){
466             mysql_query ( "unlock tables", $this->db );
467             mysql_close ( $this->db );
468         }
469     }
470 
471 }
472     
473 $db = new DbManage("localhost","root","root","dbname");
474 $db->backup("","","");
475 ?>

 

posted @ 2018-05-09 18:21  菜的掉渣  阅读(737)  评论(0编辑  收藏  举报