www.gusucode.com > iWebshop开源PHP商城系统 v2.8源码程序 > iwebshop/classes/dbbackup.php

    <?php
/**
 * @copyright (c) 2011 jooyea.cn
 * @file DBBackup.php
 * @brief 数据库备份
 * @author chendeshan
 * @date 2010-12-16
 * @version 0.6
 */

/**
 * @class DBBackup
 * @brief 数据库备份类
 */
class DBBackup
{
	private $maxLimit     = 2000;              //设置最大读取数据条数(条)
	private $partSize     = 5000;              //分卷大小(KB)
	private $ctrlRes      = array();           //要操作的资源
	private $fileName     = null;              //当前备份数据的文件名
	private $part         = 1;                 //分卷号初始值
	private $totalSize    = 0;                 //备份数据共占字节数
	private $showMess     = false;             //展示状态信息
	private $dir          = 'backup/database'; //备份路径
	private $fPrefix      = 'iwebshop';        //备份文件名前缀
	private $fExtend      = '.sql';            //备份文件扩展名

	//构造函数
	function __construct($ctrlRes = null)
	{
		$this->ctrlRes = $ctrlRes;
		if(isset(IWeb::$app->config['dbbackup']) && IWeb::$app->config['dbbackup']!=null)
			$this->dir = IWeb::$app->config['dbbackup'];

		if(!file_exists($this->dir))
		{
			IFile::mkdir($this->dir);
		}
	}

	//备份的文件列表
	function getList()
	{
		$excepArray = array('.','..','.svn');
		$fileArray  = array(
			'system'   => array(),
			'unsystem' => array(),
		);

		$dirRes = opendir($this->dir);
		while( $fileName = readdir($dirRes) )
		{
			if(!in_array($fileName,$excepArray))
			{
				if(stripos($fileName,$this->fPrefix) !== false && stripos($fileName,$this->fExtend) !== false)
					$key = 'system';
				else
					$key = 'unsystem';

				$sortKey = filectime($this->dir.'/'.$fileName);
				$fileArray[$key][$sortKey] = array(
					'name' => $fileName,
					'size' => number_format(filesize($this->dir.'/'.$fileName)/1024,1),
					'time' => date('Y-m-d H:i:s', $sortKey )
				);
				krsort($fileArray[$key]);
			}
		}
		return $fileArray;
	}

	//下载文件
	function download($file)
	{
		header('Content-Description: File Transfer');
		header('Content-Length: '.filesize($this->dir.'/'.$file));
		header('Content-Disposition: attachment; filename='.basename($file));
		readfile($this->dir.'/'.$file);
		return $this->dir.'/'.$file;
	}

	//删除数据备份文件
	function del()
	{
		foreach($this->ctrlRes as $val)
		{
			if(file_exists($this->dir.'/'.$val))
				unlink($this->dir.'/'.$val);
			else
				return false;
		}
	}

	//执行恢复
	function runRes()
	{
		foreach($this->ctrlRes as $val)
		{
			$fileName = $this->dir.'/'.$val;
			$this->parseSQL($fileName);
		}
	}

	//解析备份文件中的SQL
	function parseSQL($fileName)
	{
		$fhandle  = fopen($fileName,'r');
		while(!feof($fhandle))
		{
			$lstr = fgets($fhandle);     //获取指针所在的一行数据

			//判断当前行存在字符
			if(isset($lstr[0]) && $lstr[0]!='#')
			{
				$prefix = substr($lstr,0,2);  //截取前2字符判断SQL类型
				switch($prefix)
				{
					case '--' :
					case '//' :
					{
						continue;
					}

					case '/*':
					{
						if(substr($lstr,-5) == "*/;\r\n" || substr($lstr,-4) == "*/\r\n")
							continue;
						else
						{
							$this->skipComment($fhandle);
							continue;
						}
					}

					default :
					{
						$sqlArray[] = trim($lstr);
						if(substr(trim($lstr),-1) == ";")
						{
							$sqlStr   = join($sqlArray);
							$sqlArray = array();

							$this->query($sqlStr);

							//回调函数
							$this->actionCallBack($fileName);
						}
					}
				}
			}
		}
	}

	//略过注释
	function skipComment($fhandle)
	{
		$lstr = fgets($fhandle,4096);
		if(substr($lstr,-5) == "*/;\r\n" || substr($lstr,-4) == "*/\r\n")
			return true;
		else
			$this->skipComment($fhandle);
	}

	//执行SQL
	function query($sql)
	{
		//创建数据库对象
		$dbObj = IDBFactory::getDB();
		$dbObj->query($sql);
	}

	//打包下载
	function packDownload()
	{
		if(class_exists('ZipArchive'))
		{
			$fileName = $this->fPrefix.'_'.date('Ymd_His').'.zip';
			$zip = new ZipArchive();
			$zip->open($this->dir.'/'.$fileName,ZIPARCHIVE::CREATE);
			foreach($this->ctrlRes as $file)
			{
				$attachfile = $this->dir.'/'.$file;
				$zip->addFile($attachfile,basename($attachfile));
			}
			$zip->close();

			return $fileName;
		}
		else
		{
			return false;
		}
	}

	//动作执行回调函数
	function actionCallBack($mess)
	{
		//防止超时
		set_time_limit(60);
	}

	//设置展示状态开关
	function setShowMess($isOpen = false)
	{
		$this->showMess = $isOpen;
	}

	//设置备份的路径
	function setDir($dir)
	{
		$this->dir = $dir;
	}

	//设置分卷大小(KB)
	function setPartSize($size)
	{
		$this->partSize = $size;
	}

	//设置最大读取数据条数(条)
	function setMaxLimit($maxLimit)
	{
		$this->maxLimit = $maxLimit;
	}

	//执行备份
	function runBak()
	{
		//循环表
		foreach($this->ctrlRes as $name)
		{
			$tableStruct = $this->createStructure($name);//生成表结构
			$sumTime     = $this->countTime($name);      //计算写入文件的总次数

			//生成表数据
			$tableData   = '';
			for($time = 0;$time < $sumTime;$time++)
			{
				$offset = $time * $this->maxLimit;        //计算读取开始偏移值
				$data   = $this->getData($name,$offset);  //根据偏移值获取数据
				foreach($data as $rs)
				{
					$tableData.= $this->createInsert($name,$rs);
				}

				//表结构和$time次的表数据 总和
				$content = $tableStruct.$tableData;

				//判断文件是否溢出,如果溢出则分卷
				if($this->checkOverflow(strlen($content)))
				{
					$this->part+=1;
				}

				$this->writeFile($this->getFilename(),$content); //写入文件

				//清空数据
				$tableStruct = '';
				$tableData   = '';
			}
			//回调函数
			$this->actionCallBack($name);
		}
	}

	//写入文件
	function writeFile($fileName,$content)
	{
		$fileObj = new IFile($fileName,'a+');
		$fileObj->write($content);
	}

	//检测文件是否存放的数据是否溢出
	function checkOverflow($cSize)
	{
		$this->totalSize+=$cSize;
		if($this->totalSize >= ($this->partSize<<10)*$this->part)
			return true;
		else
			return false;
	}

	//生成文件名
	function getFilename()
	{
		if($this->fileName === null)
		{
			//获取当前时间:年月日_时分秒
			$nowTime = date('Ymd_His');
			$this->fileName = $this->dir.'/'.$this->fPrefix.'_'.$nowTime.'_'.rand(1000000,9999999);
			return $this->fileName.'_'.$this->part.$this->fExtend;
		}
		else
			return $this->fileName.'_'.$this->part.$this->fExtend;
	}

	//获取分段数据(数据库)
	function getData($name,$offset=0)
	{
		//创建数据库对象
		$dbObj = IDBFactory::getDB();

		//获取从$start至$limitNum这段数据
		$sql   = 'SELECT * FROM '.$name.' LIMIT '.$offset.','.$this->maxLimit;
		$data  = $dbObj->query($sql);
		return $data;
	}

	//计算$name数据表写入次数(数据库)
	function countTime($name)
	{
		$dbObj = IDBFactory::getDB();

		//获取数据表总的数据条数
		$sql      = 'SELECT COUNT(*) as num FROM '.$name;
		$numArray = $dbObj->query($sql);
		$dataNum  = $numArray[0]['num'];

		//计算读取的分页数
		if($dataNum > 0)
			return ceil($dataNum/$this->maxLimit);
		else
			return 1;
	}

	//创建$name数据表中的$data数据的INSERT SQL语句
	function createInsert($name,$data)
	{
		$insertSql = 'INSERT INTO `'.$name.'` VALUES( ';
		foreach($data as $key => $val)
		{
			if(is_int($key)) continue;
			$insertSql.= '\''.addslashes($val).'\',';
		}
		$insertSql = rtrim($insertSql,',');
		$insertSql.= " ); \r\n";
		return $insertSql;
	}

	//创建$name数据表结构的SQL语句(数据库)
	function createStructure($name)
	{
		//创建数据库对象
		$dbObj = IDBFactory::getDB();

		//获取$name表信息
		$tableInfoArray = $dbObj->query('SHOW TABLE STATUS WHERE Name = \''.$name.'\'');
		$tableInfo  = $tableInfoArray[0];

		//获取$name表的字段信息
		$columnInfo = $dbObj->query('SHOW FULL COLUMNS FROM `'.$name.'`');
		$columnNum  = count($columnInfo);

		//SQL初始化拼接字符串
		$bakContent = "DROP TABLE IF EXISTS `".$name."`;\r\n CREATE TABLE `".$name."` (\r\n";

		//SQL拼接$name表的字段信息
		foreach($columnInfo as $key => $value)
		{
			//SQL字段和类型
			$bakContent.='`'.$value['Field'].'` '.$value['Type'];

			//SQL是否为空
			$bakContent.=($value['Null']==='NO') ? ' NOT NULL ':' NULL ';

			//SQL索引
			if($value['Key'])
			{
				switch($value['Key'])
				{
					case "PRI":
					$bakContent.=' PRIMARY KEY ';
					break;

					case "MUL":
					$bakContent.='';
					break;

					case "UNI":
					$bakContent.=' UNIQUE KEY ';
					break;
				}
			}

			//SQL默认值
			if($value['Default']!='')
				$bakContent.=' DEFAULT '.$value['Default'];
			else if($value['Null']==='YES' && $value['Default']==NULL)
				$bakContent.=' DEFAULT NULL ';

			//SQL额外
			$bakContent.=($value['Extra']==null)   ? '' : $value['Extra'];

			//SQL注释
			$bakContent.=($value['Comment']==null) ? '' : ' COMMENT \''.addslashes($value['Comment']).'\'';
			$bakContent.=(($key+1) == $columnNum)  ? '': ",\r\n";
		}
		//SQL字段结构结束
		$bakContent.="\r\n )";

		//SQL拼接$name表信息
		$bakContent.=($tableInfo['Engine']!==null) ? ' ENGINE='.$tableInfo['Engine'] : '';
		if($tableInfo['Collation']!=null)
		{
			$collation = explode('_',$tableInfo['Collation']);
			$charset   = $collation[0];
		}
		else
			$charset   = '';

		$bakContent.=$charset ? ' CHARSET='.$charset : '';
		$bakContent.=($tableInfo['Comment']!==null)        ? ' COMMENT=\''.$tableInfo['Comment'].'\'' : '';
		$bakContent.=($tableInfo['Auto_increment']!==null) ? ' AUTO_INCREMENT='.$tableInfo['Auto_increment'] : '';
		$bakContent.=";\r\n\r\n";
		return $bakContent;
	}
}