mirror of
https://gitee.com/ShopeX/OMS
synced 2026-03-23 02:45:33 +08:00
563 lines
18 KiB
PHP
563 lines
18 KiB
PHP
<?php
|
|
/**
|
|
* Copyright 2012-2026 ShopeX (https://www.shopex.cn)
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
|
|
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);
|
|
}
|
|
}
|
|
//*/
|
|
}
|