versionInfo = require_once($versionPath); } } // 版本信息 private function version_info($param) { $paramStr = func_param_pack($param, "&"); //参数串 $foxcmsDomain = config("adminconfig.version_domain"); //版本地址 $foxcmsPathUrl = $foxcmsDomain . url('api/Version/info') . "?{$paramStr}"; $foxcmsPathUrl = replaceSymbol($foxcmsPathUrl); $resJson = get_url_content($foxcmsPathUrl); return $resJson; } // 查询版本下载信息 private function version_getinfo($param) { $paramStr = func_param_pack($param, "&"); //参数串 $foxcmsDomain = config("adminconfig.version_domain"); //版本地址 $foxcmsPathUrl = $foxcmsDomain . url("api/Version/getInfo") . "?{$paramStr}"; $foxcmsPathUrl = replaceSymbol($foxcmsPathUrl); $resJson = get_url_content($foxcmsPathUrl); return $resJson; } // 版本文件 private function version_files($param) { $paramStr = func_param_pack($param, "&"); //参数串 $foxcmsDomain = config("adminconfig.version_domain"); //版本地址 $foxcmsPathUrl = $foxcmsDomain . url("api/Version/files") . "?{$paramStr}"; $foxcmsPathUrl = replaceSymbol($foxcmsPathUrl); $resJson = get_url_content($foxcmsPathUrl); return $resJson; } // 检查下一版本 private function version_checkVersion($param) { $paramStr = func_param_pack($param, "&"); //参数串 $foxcmsDomain = config("adminconfig.version_domain"); //版本地址 $foxcmsPathUrl = $foxcmsDomain . url("api/Version/checkVersion") . "?{$paramStr}"; $foxcmsPathUrl = replaceSymbol($foxcmsPathUrl); $resJson = get_url_content($foxcmsPathUrl); return $resJson; } // 版本列表 public function index() { $param = $this->request->param(); if ($this->request->isAjax()) { $param['domain'] = $this->domainNo; $resJson = $this->version_info($param); if (empty($resJson)) { $this->success('查询成功', '', []); } $res = json_decode($resJson); $data = $res->data; //授权信息 //查询所有版本记录 $vrList = VersionRecord::select(); // $versionInfo $list = []; foreach ($data as $d) { $rd = get_object_vars($d); $remote_version_id = $rd['id']; $iscopy = 0; //副本 foreach ($vrList as $vr) { if ($vr['remote_version_id'] == $remote_version_id) { $iscopy = 1; break; } } $iscur = false; if ($rd['only'] == $this->versionInfo['only']) { $iscur = true; } $rd['iscur'] = $iscur; $rd['iscopy'] = $iscopy; array_push($list, $rd); } $this->success('查询成功', '', $list); } return view(); } // 更新单个文件 public function singleUpdateFile() { $id = $this->request->param('id'); $only = $this->request->param("only"); $resJson = $this->version_files(['id' => $id, 'domain' => $this->domainNo, 'only' => $only]); $res = json_decode($resJson); $data = $res->data; //授权信息 $dirs = []; //目录 $rdata = []; $is_sql = 0; //是否存在sql文件 foreach ($data as $d) { $file_relative_path = $d->file_relative_path; if ($is_sql == 0) { if (str_ends_with($file_relative_path, "cover.php")) { $is_sql = 1; } } $update_file = root_path() . $file_relative_path; //要更新的文件 $update_file = replaceSymbol($update_file); $is_update = 0; //新增 if (file_exists($update_file)) { $is_update = 1; //更新 } array_push($rdata, ['file_relative_path' => $file_relative_path, 'update_file_relative_path' => $file_relative_path, 'is_update' => $is_update]); $curDirArr = explode("/", $file_relative_path); unset($curDirArr[(sizeof($curDirArr) - 1)]); $update_dir = implode("/", $curDirArr); $curDir = root_path() . $update_dir . "/"; $curDir = replaceSymbol($curDir); $isExist = false; foreach ($dirs as $dir) { if ($dir['dir'] == $curDir) { $isExist = true; break; } } if (!$isExist) { $writeable = new_is_writeable($curDir); if (empty($update_dir)) { $update_dir = get_app_name(); } if ($writeable == 0) { if (!tp_mkdir($curDir)) { $writeable = 0; } else { $writeable = 1; } } array_push($dirs, ['dir' => $update_dir, 'writeable' => $writeable]); } } View::assign("is_sql", $is_sql); //是否存在sql文件 View::assign("uFiles", $rdata); $only = $this->request->param('only'); View::assign("only", $only); //下载标识 //文件夹路径权限 View::assign("dirs", $dirs); //版本id View::assign("id", $id); return view('update_file'); } // 更新文件 public function updateFile() { $param = $this->request->param(); //查询当前版本 $resJson = $this->version_info(['id' => $param['id'], "domain" => $this->domainNo]); $is_auth = 1; if (!empty($resJson)) { $res = json_decode($resJson); $data = $res->data; if (sizeof($data) > 0) { $param['only'] = $data[0]->only; $is_auth = $data[0]->is_auth; } } if ($is_auth == 0) { $noDomainUrl = "noauthorize"; $this->success("未授权调整", "", $noDomainUrl); } elseif ($this->request->isAjax()) { $this->checkUpdateVersion($param['id'], $param['only'], $this->domainNo); $this->success("确认更新"); } else { $singleUrl = url("VersionUpdate/singleUpdateFile", $param); $this->redirect($singleUrl); exit(); } } // 检查sql更新文件是否备份 public function checkBackupSql() { $beforeTime = time() - 1 * 60 * 60; //前一个小时数据,time()方法返回的秒 $backList = Db::name('data_backup')->whereTime('create_time', '>=', strtotime(date('Y-m-d H:i:s', $beforeTime)))->select(); $is_back = (sizeof($backList) > 0) ? 1 : 0; $this->success("判断是否存在sql文件", "", $is_back); } // 备份全部文件 public function backupAll() { $time = date('Y-m-d'); $path = app()->getRootPath() . 'data' . DIRECTORY_SEPARATOR . 'backupdata' . DIRECTORY_SEPARATOR . $time . DIRECTORY_SEPARATOR; if (!tp_mkdir($path)) { $this->error("创建文件夹失败"); } $dataBackup = new \app\common\model\DataBackup(); $cDataBackup = $dataBackup->where(['backup_file' => $time])->find(); if ($cDataBackup) { $pid = $cDataBackup->id; } else { $dataBackup->save(['pid' => 0, 'backup_file' => $time, 'backup_file_path' => $path, 'table_name' => $time]); if (empty($dataBackup->id)) { $this->error("操作失败"); } $pid = $dataBackup->id; } //查询数据库所有表 $sql = 'SHOW TABLE STATUS'; $list = Db::query($sql); $tableList = []; foreach ($list as $table) { $table_name = $table['Name']; if (!in_array($table_name, $this->filterTables)) { array_push($tableList, $table_name); } } $saveAll = []; foreach ($tableList as $tableName) { $random_num = func_random_num(18); $fileName = $tableName . "-" . $random_num . ".sql"; $content = (new Basckup())->backupTable($tableName); //模型表与数据内容 $fpath = write($path, $fileName, $content); //存入模型字段数据 if (empty($fpath)) { $this->error("备份失败"); } $backup_file = $tableName . ".sql"; array_push($saveAll, ['pid' => $pid, 'backup_file' => $backup_file, 'backup_file_path' => $fpath, 'random_num' => $random_num, 'table_name' => $tableName]); } $rSaveData = (new \app\common\model\DataBackup())->saveAll($saveAll); if (sizeof($rSaveData) <= 0) { $this->error('备份失败'); } $this->success('备份成功'); } // 更新处理 public function checkUpdateVersion($id, $only, $domain = "") { $vrList = VersionRecord::where(['remote_version_id' => $id])->order(['version' => 'desc'])->select(); //更新版本记录信息 $size = sizeof($vrList); if ($size > 0) { //更新过了 $this->error("当前版本你已更新过了"); } else { //未更新 $resJson = $this->version_checkVersion(['domain' => $domain, "only" => $this->versionInfo['only']]); //判断更新版本是否匹配 if (empty($resJson)) { $this->error("未知版本,请联系客服"); } $res = json_decode($resJson); if ($res->code == 1) { //查询成功 $data = $res->data; if (!($id == $data->id && $only == $data->only)) { $this->error("不能跳版本更新"); } else { if ($data->flag == "last") { $this->error($only . "不要乱搞,已是最新版本"); } } } else { $this->error($res->msg); } } } // 下载更新 public function downloadUpdate() { $only = $this->request->param('only'); $id = $this->request->param('id'); $this->checkUpdateVersion($id, $only, $this->domainNo); //更新处理 $resJson = $this->version_getinfo(['id' => $id, 'only' => $only, 'domain' => $this->domainNo]); if (empty($resJson)) { $this->error("未知版本,请联系客服"); } $pack_file_url = ""; $res = json_decode($resJson); if ($res->code == 1) { //查询成功 $data = $res->data; if ($data) { $pack_file_url = $data->pack_file_url; } } else { $this->error($res->msg); } if (empty($pack_file_url)) { $this->error("未知版本更新失败,请联系客服"); } $fileUtil = new FileUtil(); $path = root_path() . 'data/update/backup/' . date("Y-m-d") . '/'; $path = replaceSymbol($path); $down_path = $path . '/linshi/'; if (!tp_mkdir($path)) { $this->error("创建文件夹失败"); } $filepath = $fileUtil->download($pack_file_url, "", $down_path); $filepath = replaceSymbol($filepath); if (is_file($filepath)) { $this->success("下载成功", "", $filepath); } $this->error("下载失败"); } // 解压更新文件 function unFileUpdate() { $filepath = $this->request->param("filepath", ""); if (empty($filepath)) { $this->error("解压失败"); } $zipdown = new Zipdown(); $path = root_path() . 'data/update/backup/' . date("Y-m-d") . '/'; $path = replaceSymbol($path); $down_path = $path . 'linshi/'; if (!tp_mkdir($path)) { $this->error("创建文件夹失败"); } $r = $zipdown->unZipFile($filepath, $down_path); if ($r) { @unlink($filepath); //删除 $this->success("减压成功", "", $down_path); } $this->error("减压失败"); } // 文件更新 public function fileUpdate() { $only = $this->request->param('only'); $id = $this->request->param('id'); $resJson = $this->version_getinfo(['id' => $id, 'only' => $only, 'domain' => $this->domainNo]); if (empty($resJson)) { $this->error("未知版本,请联系客服"); } $saveVersion = []; $res = json_decode($resJson); if ($res->code == 1) { //查询成功 $data = $res->data; if ($data) { $saveVersion = [ 'version' => $data->code, 'only' => $data->only ]; } } else { $this->error($res->msg); } $path = root_path() . 'data/update/backup/' . date("Y-m-d") . '/'; $path = replaceSymbol($path); $down_path = $this->request->param("down_path", ""); //版本路径 $versionPath = root_path() . 'data/update/version/'; $versionPath = replaceSymbol($versionPath); if (!tp_mkdir($versionPath)) { $this->error("创建文件夹失败"); } $resJson = $this->version_files(['id' => $id, 'domain' => $this->domainNo]); $res = json_decode($resJson); $data = $res->data; //授权信息 $rootPath = root_path(); //根目录 $uFiles = []; //打包旧文件 //执行sql文件 $sqlFileArr = []; //更新的sql文件 $oldBackPath = ""; foreach ($data as $d) { $file_relative_path = $d->file_relative_path; $full_path = $rootPath . $file_relative_path; $full_path = replaceSymbol($full_path); if ((strpos($file_relative_path, "back.php") !== false) || (strpos($file_relative_path, "cover.php") !== false) ) { array_push($sqlFileArr, $file_relative_path); //备份对应旧sql语句 if ((strpos($file_relative_path, "back.php") !== false)) { $new_back_path = replaceSymbol($down_path . $file_relative_path); $backInfoList = require_once($new_back_path); $oldBack = $this->restoreBackup($backInfoList, $versionPath); array_push($uFiles, $oldBack); $oldBackPath = $oldBack['full_path']; } } else { array_push($uFiles, ['full_path' => $full_path, 'file_relative_path' => $file_relative_path]); } } //打包旧文件生成副本 $filename = func_random_num(18) . date('YmdHis') . '.zip'; $zipName = $path . $filename; // 实例化类,使用本类,linux需开启zlib,windows需取消php_zip.dll前的注释 $zip = new \ZipArchive(); $res = $zip->open($zipName, \ZipArchive::OVERWRITE | \ZipArchive::CREATE); if (!$res) { $this->error('无法打开文件,或者文件创建失败'); } // 文件打包 旧文件打包 $is_exist = false; foreach ($uFiles as $file) { $full_path = $file["full_path"]; if (file_exists($full_path)) { $is_exist = true; $file_relative_path = $file["file_relative_path"]; $zip->addFile($full_path, $file_relative_path); } } //添加数据判断打包文件 if (file_exists($versionPath . "cover.php")) { $zip->addFile($versionPath . "cover.php", "data/update/version/cover.php"); } if (file_exists($versionPath . "back.php")) { $zip->addFile($versionPath . "back.php", "data/update/version/back.php"); } if (file_exists($versionPath . "info.php")) { $zip->addFile($versionPath . "info.php", "data/update/version/info.php"); } else { $this->error("更新失败,缺少当前版本信息"); } // 关闭 $zip->close(); // 验证文件是否存在 if ($is_exist) { if (!file_exists($zipName)) { $this->error("文件不存在" . $zipName); } } //删除旧备份记录 if (file_exists($oldBackPath)) { @unlink($oldBackPath); } //更新替换文件 foreach ($uFiles as $file) { $source = $down_path . $file['file_relative_path']; $source = replaceSymbol($source); if (file_exists($source)) { $dest = $file['full_path']; $rc = copy($source, $dest); if (!$rc) { $this->error("更新失败"); } } } //替换数据判断文件 $table_prefix = config("database.connections.mysql.prefix"); if (sizeof($sqlFileArr) > 0) { foreach ($sqlFileArr as $file) { $source = $down_path . $file; $source = replaceSymbol($source); if (file_exists($source)) { if ((strpos($file, "cover.php") !== false)) { copy($source, $versionPath . "cover.php"); //执行sql文件 $sqlContent = file_get_contents($source); $sqlFormat = (new ModelMg())->sql_split($sqlContent, $table_prefix); // 执行SQL语句 $counts = count($sqlFormat); for ($i = 0; $i < $counts; $i++) { $sql = trim($sqlFormat[$i]); if (trim($sql) == '') continue; try { Db::execute($sql); } catch (\Exception $e) { // $this->error($e->getMessage()); } } } elseif ((strpos($file, "back.php") !== false)) { copy($source, $versionPath . "back.php"); } } } } else { if (file_exists($versionPath . "cover.php")) { @unlink($versionPath . "cover.php"); //删除 } if (file_exists($versionPath . "back.php")) { @unlink($versionPath . "back.php"); //删除 } } //删除临时文件夹 delDir($down_path); //修改当前版本信息 set_php_arr($versionPath, 'info.php', $saveVersion); //记录更新信息 $versionRecord = VersionRecord::where(['remote_version_id' => $id])->find(); if ($versionRecord) { @unlink($versionRecord['copy_path']); //删除之前的 (new VersionRecord())->update(['id' => $versionRecord['id'], 'copy_path' => $zipName]); } else { (new VersionRecord())->save(['copy_path' => $zipName, 'remote_version_id' => $id, "version" => $saveVersion['version']]); } $this->success("更新成功", "", $saveVersion['version']); } // 备份旧的sql private function restoreBackup($backInfoList, $backPath) { if (!tp_mkdir($backPath)) { $this->error("创建文件夹失败"); } $fileName = "old_back.php"; try { if (file_exists($backPath . $fileName)) { @unlink($backPath . $fileName); //先删 } foreach ($backInfoList as $backInfo) { $type = $backInfo['type']; $table = $backInfo['table']; if (!TableUtil::check_table($table)) { //判断表是否存在 continue; } $oldDataSql = ""; if ($type == "drop") { //删除表 $oldDataStructSql = ((new Basckup())->getbackupTable($table)); write($backPath, $fileName, "{$oldDataStructSql[0]}\n"); if (sizeof($oldDataStructSql) >= 1) { write($backPath, $fileName, "{$oldDataStructSql[1]}\n"); } } elseif ($type == "update") { //更新值 $tData = Db::table($table)->field($backInfo['field'])->find($backInfo['id']); $val = $tData[$backInfo['field']]; if ($backInfo['value_type'] == "string") { $val = "'{$val}'"; } $oldDataSql = "UPDATE `{$table}` SET `{$backInfo['field']}` = {$val} WHERE `id` = {$backInfo['id']};"; } elseif ($type == "delete") { //删除某值 $oldDataSql = (new Basckup())->getBackupTableData($table, "id={$backInfo['id']}"); } write($backPath, $fileName, "{$oldDataSql}\n"); } } catch (\Exception $e) { } return ['full_path' => "{$backPath}{$fileName}", 'file_relative_path' => "data/update/version/{$fileName}"]; } // 还原 public function restoreFile() { $id = $this->request->param('id'); //记录更新信息 $versionRecord = VersionRecord::where(['remote_version_id' => $id])->find(); if (!$versionRecord) { $this->error("没有查到还原数据"); } $restoreKey = -1; $vrList = VersionRecord::order(['version' => 'desc'])->select(); //更新版本记录信息 if (sizeof($vrList) > 1) { //是否 foreach ($vrList as $key => $vr) { if ($id == $vr['remote_version_id']) { $restoreKey = $key; } } if ($restoreKey != 0) { $this->error("抱歉不能跳版本还原"); } } $copy_path = $versionRecord['copy_path']; if (!file_exists($copy_path)) { $this->error("没有找到还原数据"); } $resJson = $this->version_files(['id' => $id, 'domain' => $this->domainNo]); $res = json_decode($resJson); $data = $res->data; //授权信息 $rootPath = root_path(); //根目录 //数据判断打包文件 $versionPath = root_path() . 'data/update/version/'; $versionPath = replaceSymbol($versionPath); foreach ($data as $d) { $file_relative_path = $d->file_relative_path; $full_path = $rootPath . $file_relative_path; $full_path = replaceSymbol($full_path); if ((strpos($file_relative_path, "back.php") !== false)) { //还原执行 if (file_exists($versionPath . "back.php")) { $backInfoList = require_once($versionPath . "back.php"); $this->restoreExecute($backInfoList); } } if (file_exists($full_path)) { @unlink($full_path); //删除更新文件 } } $zipdown = new Zipdown(); $r = $zipdown->unZipFile($copy_path, $rootPath); if ($r) { @unlink($copy_path); //删除更新下载包 VersionRecord::destroy($versionRecord['id']); //删除文件信息 //执行恢复中数据 $table_prefix = config("database.connections.mysql.prefix"); if (file_exists($versionPath . "old_back.php")) { //是否存在 $sqlContent = file_get_contents($versionPath . "old_back.php"); $sqlFormat = (new ModelMg())->sql_split($sqlContent, $table_prefix); // 执行SQL语句 $counts = count($sqlFormat); for ($i = 0; $i < $counts; $i++) { $sql = trim($sqlFormat[$i]); if (trim($sql) == '') continue; Db::execute($sql); } @unlink($versionPath . "old_back.php"); //删除 } $this->success("恢复成功"); } $this->error("恢复失败"); } // 恢复操作 private function restoreExecute($backInfoList) { foreach ($backInfoList as $backInfo) { $type = $backInfo['type']; $table = $backInfo['table']; if (!TableUtil::check_table($table)) { //判断表是否存在 continue; } try { if ($type == "create") { //创建 //删表 $r = TableUtil::del_table($table); } elseif ($type == "insert") { //添加数据 //删数据 Db::table($table)->where(['id' => $backInfo['id']])->delete(); } elseif ($type == "alter") { //添加字段 //删表字段 $r = TableUtil::del_column($table, $backInfo['field']); } } catch (\Exception $e) { } } } // 说明 public function explain() { $id = $this->request->param('id'); $resJson = $this->version_files(['id' => $id, 'domain' => $this->domainNo]); $res = json_decode($resJson); if ($res->code == 0) { //未授权 if ($this->request->isAjax()) { $this->success("未授权", "", "noauthorize"); } } else { if ($this->request->isAjax()) { $this->success("授权"); } } View::assign("authorize_code", $res->code); $data = $res->data; //授权信息 $rdata = []; foreach ($data as $d) { $file_relative_path = $d->file_relative_path; array_push($rdata, ['file_relative_path' => $file_relative_path]); } View::assign("uFiles", $rdata); //版本信息 $resJson = $this->version_info(['id' => $id, 'domain' => $this->domain]); $res = json_decode($resJson); $dataVersionList = $res->data; //授权信息 $version = []; if (sizeof($dataVersionList) > 0) { $version = $dataVersionList[0]; $version = get_object_vars($version); } View::assign("version", $version); return view(); } }