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 ?>