PHP通过面向对象实现数据库备份

时间:2022-09-14 09:02:57

参照了网上一些文档,最后自己总结写出了两个类,我试了,可以实现数据库备份与上传功能
<?php
    /**
        file: dbbackup.class.php
        数据库备份类文件,备份文件放在/backup 目录下
        package: sql
    */
    date_default_timezone_set('PRC');                              //设置时区
    
    class Dbbackup {
        private $path = "sql/backup/";                           //SQL文件保存路径,默认为/backup
        private $database;                                         //所要备份的数据库的名称
        private $size;                                             //分卷分件大小
        private $fileName;                                         //SQL文件名
        
        private $ds = "\n";                                        //换行符
        private $sqlEnd = ";";                                     //每条SQL语句的结尾符号
        
        
        /**
        *构造方法,用来实例化备份类对象
        *@param    string    $database      数据库名称,默认值为‘lighting’
        *@param    int       $size          分卷文件大小,默认值为2048
        */
        function __construct($size = 2048, $database = 'lighting') {
            $this->database = $database;                           //设置所要备份的数据库,默认为 lighting
            $this->size = $size;                                   //分卷文件大小,默认为2MB
            $this->fileName = date('YmdHis')."_all";
            
            ob_end_flush();                                        //清除所有缓存
        }
        
        /**
        *公用方法,进行数据库备份
        *@return bool   是否备份成功
        */
        public function backup() {
            $sqlFile = '';                                         //准备写入数据库的信息
            
            $this->showMess("正在备份");
            /*备份指定数据库中所有的数据表*/
            if ($tables = mysql_query( "SHOW TABLE STATUS FROM ".$this->database)) {
                $this->showMess("读取数据库结构成功!");
            }
            else {
                $this->showMess("读取数据库结构失败!", true);
                exit(0);
            }
            
            $sqlFile .= $this->retrieve();                             //写入头部dump信息
            $tables = mysql_query("SHOW TABLES");                      //查询数据库中所有的表
            $paper = 1;                                                //分卷标示
            
            while($table = mysql_fetch_array($tables)) {
                $tableName = $table[0];                                //获取表名
                
                $sqlFile .= $this->insertTableStructure($tableName);   //获取表结构
                
                $data = mysql_query("SELECT * FROM ".$tableName);      //获取表中数据
                $num_fields = mysql_num_fields($data);
                
                /*遍历表中所有数据记录*/
                while($record = mysql_fetch_array($data)) {
                    $sqlFile .= $this->insertRecord($tableName, $num_fields, $record);  //单条数据记录
                    /*如果大于分卷大小,则写入文件*/
                    if($sqlFile >= $this->size*1000) {
                        $file = $this->fileName."_v".$paper.".sql";         //SQL文件名
                        
                        /*将SQL数据写入文件*/
                        if($this->writeFile($sqlFile,$file)) {
                            $this->showMess("-卷-<b>".$paper."</b>-数据备份完成,备份文件 [ <span>".$this->path.$file."</span> ]");
                        }
                        else {
                            $this->showMess("卷-<b>".$paper."</b>-备份失败",true);
                            
                            return false;
                        }
                        
                        /*下一个分卷*/
                        $paper ++;
                        /*重置$sqlFile变量为空,重新计算该变量大小*/
                        $sqlFile = "";
                    }
                    else {
                        /*如果SQL文件没有超过分卷大小,直接写入文件*/
                        $file = $this->fileName.".sql";         //SQL文件名
                        
                        if($this->writeFile($sqlFile,$file)) {
                            $this->showMess("-卷-<b>".$paper."</b>-数据备份完成,备份文件 [ <span>".$this->path.$file."</span> ]");
                        }
                        else {
                            $this->showMess("卷-<b>".$paper."</b>-备份失败",true);
                            
                            return false;
                        }
                    }
                    
                    $this->showMess("恭喜您! <span>备份成功</span>");
                }
            }
        }
         
        /**
        *类内部调用的私有方法,进行提示信息输出
        *@param string  $mess   提示信息
        *@param bool    $err    错误信息标示符,默认为false,true为输出错误信息
        */
        private function showMess($mess, $err = false) {
            if($err)
                $err = "<span>ERROR:</span>";
            else 
                $err = '';
            
            echo "<p>".$err.$mess."</p>";
        }
        
        /**
        *类内部调用的私有方法,添加数据库备份文件头部基础信息
        *@return     string     $value   头部基础信息
        */
        private function retrieve() {
            $value = '';
            $value .= '--'.$this->ds;
            $value .= '-- MySQL database dump'.$this->ds;
            $value .= '--' . $this->ds;
            $value .= '-- 主机: '.$_SERVER['SERVER_NAME'].$this->ds;
            $value .= '-- 生成日期: '.date('Y').'年 '.date('m').'月'.date('d').'日'.date('H:i').$this->ds;
            $value .= '-- MySQL版本: '.mysql_get_server_info().$this->ds;
            $value .= '-- PHP 版本: '.phpversion().$this->ds;
            $value .= $this->ds;
            $value .= '--'.$this->ds;
            $value .= '-- 数据库:`'.$this->database.'`'.$this->ds;
            $value .= '--'.$this->ds.$this->ds;
            $value .= '-- -------------------------------------------------------';
            $value .= $this->ds.$this->ds;
            
            return $value;
        }
        
        /**
        *类内部调用的私有方法,将表结构信息插入
        *@param  string  $tableName        提示信息
        *@return string  $sqlFileTable    返回表结构
        */
        private function insertTableStructure($tableName) {
            $sqlFileTable = '';
            $sqlFileTable .= "--".$this->ds;
            $sqlFileTable .= "-- 表的结构".$tableName.$this->ds;
            $sqlFileTable .= "--".$this->ds.$this->ds;
            
            /*如果数据库中存在此表,进行删除(用户数据库文件导入时)*/
            $sqlFileTable .= "DROP TABLE IF EXISTS `".$tableName.'`'.$this->sqlEnd.$this->ds;
            
            /*获取详细表信息*/
            $res = mysql_query('SHOW CREATE TABLE `'.$tableName.'`');
            $row = mysql_fetch_array($res);
            $sqlFileTable .= $row[1];
            $sqlFileTable .= $this->sqlEnd.$this->ds;
            
            /*加上表数据提示*/
            $sqlFileTable .= $this->ds;
            $sqlFileTable .= "--".$this->ds;
            $sqlFileTable .= "-- 转存表中的数据 ".$tableName.$this->ds;
            $sqlFileTable .= "--".$this->ds;
            $sqlFileTable .= $this->ds;
            
            return $sqlFileTable;
        }
        
        /**
        *类内部调用的私有方法,将表中的数据插入
        *@param string     $tableName       表名
        *@param int        $num_fields      表中数据条数
        *@param array      $record          表中的数据
        *@return    string      $insert     表中该条数据中所有字段中的数据组成的字符串
        */
        private function insertRecord($tableName, $num_fields, $record) {
            $insert = '';                                              // sql字段逗号分割
            $comma = "";
            $insert .= "INSERT INTO `".$tableName."` VALUES(";
            
            /*循环每个子段下面的内容*/
            for($i = 0; $i < $num_fields; $i++) {
                $insert .= ($comma."'".mysql_real_escape_string($record[$i])."'");
                $comma = ",";
            }
            $insert .= ");".$this->ds;
            
            return $insert;
        }
        
        /**
        *@param string $sqlFile     所要写入的SQL数据
        *@param string $filename    SQL备份文件名
        *@return    bool    $re     返回是否成功写入SQL文件
        */
        private function writeFile($sqlFile, $filename) {
            $re = true;
            if (!$fp =fopen($this->path.$filename, "x")) {
                $re = false;
                $this->showMess("打开sql文件失败!", true);
            }
            if (!fwrite($fp,$sqlFile)) {
                $re = false;
                $this->showMess("写入sql文件失败,请文件是否可写", true);
            }
            if (!fclose($fp)) {
                $re = false;
                $this->showMess("关闭sql文件失败!", true);
            }
            
            return $re;
        }
        
        /*析构函数*/
        function __destruct() {
            echo "再见<br />";
        }
    }
    
?>
<?php
    /**
        file: dbupdata.class.php
        数据库更新类文件,将放在/backup 文件下的对应SQL文件更新到数据库
        package: sql
    */
    class Dbupdata {
        private $fileName;                                          //需要上传的文件的名称
        private $path = "sql/backup/";                            //SQL文件所在路径,默认为/backup
        private $database;                                          //所要上传到的数据库
        
        
        /**
        *构造方法,用来实例化备份类对象
        *@param    string    $fileName      SQL文件名称
        */
        function __construct($fileName) {
            $this->database = "lighting";                           //操作数据库名,默认为lighting
            $this->fileName = $fileName;
        }
        
        /*公共函数,用于进行数据文件上传*/
        public function restore() {
            if(!file_exists($this->path.$this->fileName)) {
                $this->showMess("SQL文件不存在! 请检查", true);
                
                exit ();
            }
            
            /*检测是否包含分卷,将类似20120516211738_all_v1.sql从_v分开,有则说明有分卷*/
            $volume = explode("_v", $this->fileName);
            $volume_path = $volume[0];
            
            $this->showMess("请勿刷新及关闭浏览器以防止程序被中止,如有不慎!将导致数据库结构受损");
            $this->showMess("正在导入备份数据,请稍候!");
            
            /*无分卷sql文件导入*/
            if(count($volume) == 1) {
                $this->showMess("正在导入sql:<span>".$this->fileName.'</span>');
                
                if($this->importInto($this->fileName)) {
                    $this->showMess("数据库导入成功! ");
                }
                else {
                    $this->showMess("数据库导入失败!", true);
                    exit();
                }
            }
            /*有分卷导入*/
            else {
                /*获取当前是第几分卷,循环执行余下分卷*/
                $volume_id = explode(".sq", $volume[1]);
                /*当前分卷为$volume_id*/
                $volume_id = intval($volume_id[0]);                  //将string强制转换为int型
                
                while($volume_id) {
                    $tmpfile = $volume_path."_v".$volume_id.".sql";  //构造当前分卷文件名
                    
                    /*如果存在分卷就继续执行*/
                    if(file_exists($this->path.$tmpfile)) {
                        $this->showMess("正在导入分卷".$volume_id.':'.$tmpfile."<br />");
                        if($this->importInto($tmpfile)) {
                            $this->showMess("分卷".$volume_id.':'.$tmpfile."导入成功!<br />");
                        }
                        else {
                            $this->showMess("导入分卷<span style='color:#f00;'>".$tmpfile."</span>失败!可能是数据库结构已被破坏,请尝试从分卷一开始导入。", true);
                        }
                    }
                    else {
                        $this->showMess("分卷备份全部导入成功!");
                    }
                    
                    $volume_id ++;                                   //下一个分卷
                }
            }
        }
        
        /**
        *类内部调用的私有方法,进行提示信息输出
        *@param string  $mess   提示信息
        *@param bool    $err    错误信息标示符,默认为false,true为输出错误信息
        */
        private function showMess($mess, $err = false) {
            if($err)
                $err = "<span>ERROR:</span>";
            else 
                $err = '';
            
            echo "<p>".$err.$mess."</p>";
        }
        
        /**
        *类内部调用的私有方法,将SQL导入数据库
        *@param string $sqlFile     导入的SQL文件名
        *@return    bool            返回写入是否成功,如果成功则返回true
        */
        private function importInto($sqlFile) {
            /*读取SQL文件*/
            $f = fopen($this->path.$sqlFile, "rb");
            /*创建表缓冲变量*/
            $create_table = '';
            while(!feof($f)) {
                /*读取每一行sql*/
                $line = fgets($f);
                
                /*这一步为了将创建表合成完整的sql语句*/
                //如果结尾没有包含';'(即为一个完整的sql语句,这里是插入语句),并且不包含'ENGINE='(即创建表的最后一句)
                if (!preg_match('/;/', $line) || preg_match('/ENGINE=/', $line)) {
                    /*将本次sql语句与创建表sql连接存起来*/
                    $create_table .= $line;
                    
                    /*如果包含了创建表的最后一句*/
                    if (preg_match('/ENGINE=/', $create_table)) {
                        /*执行sql语句创建表*/
                        $this->insertInto($create_table);
                        
                        /*清空当前,准备下一个表的创建*/
                        $create_table = '';
                    }
                    
                    /*跳过本次*/
                    continue;
                }
                
                /*执行sql语句*/
                $this->insertInto($line);
            }
            fclose ($f);
            
            return true;
        }
        
        /**
        *类内部调用的私有方法,插入单条sql语句
        *@return    bool    是否插入成功,成功返回true
        */
        private function insertInto($sql){
            if (!mysql_query(trim($sql))) {
                $this->showMess(mysql_error(), true);
                
                return false;
            }
        }
        
        /*析构函数*/
        function __destruct() {
            echo "再见<br />";
        }
    }
    
?>