Files
OMS/app/desktop/lib/system/backup.php
2025-12-28 23:13:25 +08:00

552 lines
18 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Copyright © ShopeX http://www.shopex.cn. All rights reserved.
* See LICENSE file for license details.
*/
class desktop_system_backup {
private $header = "<?php exit; ?>"; //追加头文件内容
private $tar_ext = '.zip'; //下载扩展名
private $backdir; //备份目录
private $prefilename = 'multibak_';
/**
* __construct
* @return mixed 返回值
*/
public function __construct() {
$this->backdir = DATA_DIR.'/backup';
}
/**
* 获取
* @param mixed $var var
* @return mixed 返回结果
*/
public function get($var='') {
if(!$var) return false;
return $this->$var;
}
/**
* 获取List
* @return mixed 返回结果
*/
public function getList(){
$dir = $this->backdir;
if(!is_dir($dir))return false;
$handle=opendir($dir);
if ($handle = opendir($dir)) {
$return = array();
while (false !== ($file = readdir($handle))) {
if($file[0]=='.') continue;
if( is_file($dir.'/'.$file) ){
//备份时间取文件名字
$array = explode( '.',$file );
$temp = array();
if( count($array)==3 ) {
$temp['app'] = $array[0];
$nfilename = $array[1];
} else {
$nfilename = $array[0];
$temp['app'] = '全局备份';
}
$datetime = ltrim($nfilename,$this->prefilename);
if( strlen($datetime)!=14 ) continue;
$datetime = mktime( substr($datetime,8,2) ,
substr($datetime,10,2) ,
substr($datetime,12,2) ,
substr($datetime,4,2) ,
substr($datetime,6,2) ,
substr($datetime,0,4)
);
$temp['name'] = str_replace('.php', $this->tar_ext, $file);
$temp['size'] = filesize($dir .'/'. $file);
$temp['time'] = $datetime;
if( end($array)==trim($this->tar_ext,'.') ) {
$this->convertFile( $dir,$file );
@unlink($dir.'/'.$file);
}
$return[] = $temp;
}
}
krsort($return);
closedir($handle);
}
return $return;
}
/**
* uninstall_backup
* @param mixed $app app
* @return mixed 返回值
*/
public function uninstall_backup( $app ) {
$oMysqlDump = kernel::single("desktop_system_mysqldumper");
$backdir = $this->backdir;
$dirname = date("Ymdhis");
is_dir($backdir) or mkdir($backdir, 0755, true);
$oMysqlDump->multi_dump_sdf( $app,$backdir .'/' . $dirname );
$this->create_tar( $backdir,$dirname,$app );
}
/**
* start_backup_sdf
* @param mixed $params 参数
* @param mixed $nexturl nexturl
* @return mixed 返回值
*/
public function start_backup_sdf(&$params,&$nexturl){
set_time_limit(0);
header("Content-type:text/html;charset=utf-8");
$app = $params['appname'];
$dirname = $params['dirname'];
#$cols = $params['cols'];
#$model = $params['model'];
#$startid = $params['startid'];
$oMysqlDump = kernel::single("desktop_system_mysqldumper");
$backdir = $this->backdir;
$oMysqlDump->tableid = $tableid;
$oMysqlDump->startid = $startid;
is_dir($backdir) or mkdir($backdir, 0755, true);
is_dir($backdir .'/' . $dirname) or mkdir($backdir .'/' . $dirname, 0755, true);
$app = $oMysqlDump->multi_dump_sdf( $app,$backdir .'/' . $dirname );
if($app){
$nexturl = "index.php?app=desktop&ctl=backup&act=backup_sdf&appname=$app&dirname=$dirname";
$params['app'] = $app;
} else {
return $this->create_tar( $backdir,$dirname );
}
return false;
}
/**
* 生成tar包
*/
private function create_tar( $backdir,$dirname,$app='' ) {
$tar = kernel::single("base_tar");
$dir = $backdir. '/' . $dirname;
chdir($dir);
$size = $this->add_tar( $dir,$tar );
$size = ceil($size/1024/1024) + 128;
ini_set("memory_limit", "{$size}M");
$tar->filename = ($app?$app.'.':''). $this->prefilename.$dirname.$this->tar_ext;
$tar->saveTar();
@copy($dir . '/' . $tar->filename, $backdir.'/'.$tar->filename);
$this->convertFile($backdir, $tar->filename, '.php');
if(is_resource($tar->tar_file)) {
fclose($tar->tar_file);
}
@unlink($tar->filename);
@unlink($backdir.'/'.$tar->filename);
$dir = $backdir. '/' . $dirname;
chdir($dir);
if ($handle = opendir($dir)) {
while (false !== ($file = readdir($handle))) {
if(is_file($file)) {
@unlink($file);
}
}
closedir($handle);
}
utils::remove_p($dir);
return true;
}
private function add_tar( $dir,&$tar,$parentdir='' ) {
$size = 0;
if ($handle = opendir($dir)) {
while (false !== ($file = readdir($handle))) {
if($file[0]!='.') {
if( is_file($dir.'/'.$file) ) {
$size += filesize($parentdir.$file);
$tar->addFile($parentdir.$file);
} else {
$tar->addDirectory($parentdir.$file);
$size += $this->add_tar( $dir.'/'.$file, $tar, $parentdir.$file.'/' );
}
}
}
closedir($handle);
}
return $size;
}
/**
* download
* @param mixed $file file
* @return mixed 返回值
*/
public function download($file) {
$dir = DATA_DIR.'/backup/';
$file = str_replace($this->tar_ext, '.php', $file);
if(!file_exists($dir . $file)){
return false;
}
$etag = md5_file($dir . $file);
header('Etag: '.$etag);
if(isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] == $etag){
header('HTTP/1.1 304 Not Modified',true,304);
exit(0);
}else{
set_time_limit(0);
ob_end_clean();
$content_size = filesize($dir . $file) - strlen($this->header);
$filename = substr($file, 0, strrpos($file, '.')) . $this->tar_ext;
header("Content-Disposition: attachment; filename=\"$filename\"");
header("Content-Length: " . $content_size);
$handle = fopen($dir . $file, "r");
$flag = false;
while($buffer = fread($handle,102400)){
if(!$flag) $buffer = str_replace($this->header, '', $buffer);
echo $buffer;
$flag = true;
flush();
}
fclose($handle);
}
}
function recover($sTgz,&$vols,$fileid,&$pre_app){
$prefix = substr($sTgz,0,23);
$sTmpDir = DATA_DIR.'/tmp/'.md5($sTgz).'/';
$sTgz = str_replace($this->tar_ext, '.php', $sTgz);
if($fileid==1){
$vols = 0;
$rTar = kernel::single("base_tar");
is_dir($sTmpDir) or mkdir($sTmpDir, 0755, true);
$file = DATA_DIR.'/backup/'.$sTgz;
$size = filesize($file);
$size = ceil($size/1024/1024) + 128;
ini_set("memory_limit", "{$size}M");
$newFile = $this->convertFile(DATA_DIR.'/backup/', $sTgz, $this->tar_ext);
if($rTar->openTAR($newFile, null)){
if(!$rTar->files) return false;
foreach($rTar->files as $id => $aFile) {
if( substr($aFile['name'],0,4)=='sdf/') $vols++;
$sPath=$sTmpDir.$aFile['name'];
is_dir( dirname($sPath) ) or mkdir( dirname($sPath),0755,true );
file_put_contents($sPath,$rTar->getContents($aFile));
chmod($sPath,0755);
}
}
$rTar->closeTAR();
@unlink($newFile);
return $this->comeback($sTmpDir, 1, $vols, $pre_app);
}else{
return $this->comeback($sTmpDir, $fileid, $vols, $pre_app);
}
}
function comeback($sDir, $fileid=1, $vols, &$pre_app){
$dir = $sDir;
$sDir = $sDir .'/sdf';
if(!is_dir($sDir)) return false;
chdir($sDir);
if($rHandle=opendir($sDir)){
while(false!==($sFile=readdir($rHandle))){
if($sFile[0]=='.')continue;
$handle = fopen($sFile,'r');
$app = substr($sFile, 0, strpos($sFile, '.'));
$model = substr($sFile, strpos($sFile, '.')+1, -(strpos(strrev($sFile), '.') + 1));
$model = strpos($model, '.') ? substr($model, 0, strpos($model, '.')) : $model;
if(!$app || !$model) return false;
if( app::get($app)->is_actived() ) { //APP安装了继续跑
$o = app::get($app)->model($model);
$this->app_update( $dir,$app,$pre_app,$model,$o );
$str = null;
while($sdf=$this->fgetline($handle)){
if(!is_array(@unserialize($sdf))){
$buffer .= $sdf;
$sdf = @unserialize($buffer);
if(!is_array($sdf)) continue;
}else{
$sdf = @unserialize($sdf);
}
if( $app=='base' && $model=='app_content' ) {
if( !$sdf['app_id'] ) continue;
if( !isset($arr_app_status[$sdf['app_id']]) )
$arr_app_status[$sdf['app_id']] = app::get($sdf['app_id'])->is_actived();
if( !$arr_app_status[$sdf['app_id']] ) continue;
}
//删除kvstore主键 避免冲突 session
if( $app=='base' && $model=='kvstore' ) unset( $sdf['id'] );
$return = @$o->insert($sdf);
$buffer = null;
}
} else {
$this->show_message .= 'app: '.$app .' 没有安装!数据无法还原!<BR />';
}
fclose($handle);
@unlink($sFile);
$pre_app = $app;
break;
}
closedir($rHandle);
if( !$sFile ) {
$this->app_update( $dir,$app,$pre_app );
utils::remove_p($sDir);
}
if( $fileid==$vols ) {
$this->post_comeback($dir);
utils::remove_p($dir);
}
}
}
//////////////////////////////////////////////////////////////////////////
//数据还原之后
///////////////////////////////////////////////////////////////////////////
private function post_comeback($dir) {
if($rHandle=opendir($dir.'/dbschema/')){
while(false!==($app=readdir($rHandle))){
if( $app[0]=='.' ) continue;
if( !is_dir($dir.'/dbschema/'.$app) ) continue;
#if( $handle=opendir($dir.'/dbschema/'.$app) ) {
# while(false!==($sFile=readdir($handle))){
# if( $sFile[0]=='.' ) continue;
# $tmp_model = substr($sFile,0,strpos($sFile,'.'));
//最后会修复表结构到最新 以下没有意义
#$dbschema_file = $dir.'/dbschema/'.$app.'/'.$sFile;
#if( $db[$tmp_model]['unbackup'] ) continue;
#$this->create_table( $pre_app,$tmp_model,$o,$dbschema_file );
# }
#}
if( !$this->dbtable )
$this->dbtable = kernel::single('base_application_dbtable');
$this->dbtable->update($app);
}
}
}
private function app_update( $dir,$app,$pre_app,$model='',$o='' ) {
if( !$app ) return false;
$dbschema_file = $dir.'/dbschema/'.$app.'/'.$model.'.php';
$dbschema_file_bak = $dir.'/dbschema/'.$app.'/'.$model.'.bak.php';;
if( is_file($dbschema_file) ) {
$this->create_table( $app,$model,$o,$dbschema_file );
#unlink( $dbschema_file );
rename($dbschema_file,$dbschema_file_bak);
} else if(is_file($dbschema_file_bak)) {
$this->create_table( $app,$model,$o,$dbschema_file_bak,false );
}
}
/*
* 创建数据表
*/
private function create_table( $app,$model,$o,$dbschema_file,$createtable=true ) {
if( !$app ) return false;
if( !$model ) return false;
require( $dbschema_file );
if( !$this->dbtable )
$this->dbtable = kernel::single('base_application_dbtable');
$this->dbtable->target_app = app::get($app);
//是否默认使用innodb
$this->dbtable->_enable_innodb = 'YES';
$this->dbtable->key = $model;
$real_table_name = $this->dbtable->real_table_name();
$define = $db[$model];
$this->get_defined_dbsdf( $define );
$this->dbtable->_define[$real_table_name] = $define;
if( $createtable ) {
$sql = $this->dbtable->get_sql("sdb_{$app}_{$model}");
$o->db->exec("DROP TABLE IF EXISTS sdb_{$app}_{$model}");
$o->db->exec($sql);
} else {
}
}
private function get_defined_dbsdf( &$define ) {
foreach($define['columns'] as $k=>$v){
if($v['pkey'])
$define['idColumn'][$k] = $k;
if($v['is_title'])
$define['textColumn'][$k] = $k;
if($v['in_list']){
$define['in_list'][] = $k;
if($v['default_in_list']){
$define['default_in_list'][] = $k;
}
}
$define['columns'][$k] = $this->dbtable->_prepare_column($k, $v);
if(isset($v['pkey']) && $v['pkey']){
$define['pkeys'][$k] = $k;
}
}
if(!$define['idColumn']){
$define['idColumn'] = key($define['columns']);
}elseif(count($define['idColumn'])==1){
$define['idColumn'] = current($define['idColumn']);
}
if(!$define['textColumn']){
$keys = array_keys($define['columns']);
$define['textColumn'] = $keys[1];
}elseif(count($define['idColumn'])==1){
$define['textColumn'] = current($define['textColumn']);
}
}
/**
* 文件类型转换
*/
private function convertFile($dir, $file, $type='.php') {
$dir = rtrim($dir, '/') . '/';
$new_file_name = substr($file, 0, -(strpos(strrev($file), '.')+1)) . $type;
$newFile = $dir . $new_file_name;
$flag = true;
if($type==$this->tar_ext) { //还原时从php转换成tar包
$flag = false;
//is_dir($dir .'/tmptar/') or mkdir($dir .'/tmptar/');
//$newFile = $dir .'/tmptar/'. md5($newFile);
if(file_exists($newFile)) { //如果是tar文件转换成php形式
$this->convertFile( $dir,$new_file_name );
}
}
if(file_exists($newFile)) {
return $newFile;
}
$handle = fopen($newFile, 'x');
if($flag) {
fwrite($handle, $this->header);
}
$src = fopen($dir . $file, 'r');
while (!feof($src)) {
$contents = fgets($src);
if(!$flag && strpos($contents, $this->header)!==false) $contents = substr($contents, strlen($this->header));
if($contents) {
fwrite($handle, $contents);
}
$flag = true;
}
fclose($src);
fclose($handle);
return $newFile;
}
function fgetline($handle){
$buffer = fgets($handle, 4096);
if (!$buffer){
return false;
}
if(( 4095 > strlen($buffer)) || ( 4095 == strlen($buffer) && "\n" == $buffer[4094] )){
$line = $buffer;
}else{
$line = $buffer;
while( 4095 == strlen($buffer) && "\n" != $buffer[4094] ){
$buffer = fgets($handle,4096);
$line.=$buffer;
}
}
return $line;
}
function removeTgz($sFile){
#foreach($aTgz as $sFile){
$pathinfo = pathinfo($sFile);
@unlink(DATA_DIR.'/backup/'.$pathinfo['filename'].'.php');
#}
return true;
}
function __finish($sDir){
$this->__removeDir($sDir);
return $sDir;
}
function __removeDir($sDir){
if($rHandle=opendir($sDir)){
while(false!==($sItem=readdir($rHandle))){
if ($sItem!='.' && $sItem!='..'){
if(is_dir($sDir.'/'.$sItem)){
$this->__removeDir($sDir.'/'.$sItem);
}else{
@unlink($sDir.'/'.$sItem);
}
}
}
closedir($rHandle);
utils::remove_p($sDir);
}
}
//*/
}