branch = $this->config('upgrade_branch') == '3.X.dev' ? '3.X.dev' : '3.X';
$this->force = $this->config('upgrade_force') ?: 0;
$this->revise = $this->config('revise_version') ?: 0;
}
public function index()
{
switch (get('action')) {
case 'local':
$upfile = $this->local();
break;
default:
$upfile = array();
}
$this->assign('upfile', $upfile);
$this->assign('branch', $this->branch);
$this->assign('force', $this->force);
$this->assign('revise', $this->revise);
$this->assign('snuser', $this->config('sn_user') ?: 0);
$this->assign('site', get_http_url());
$this->display('system/upgrade.html');
}
public function checkCache(){
$now = time();
$cache = $_SESSION['check_cache'];
if(!$cache){
$_SESSION['check_cache'] = time();
json(1,'');
}else{
$sub = $now - $cache;
if($sub > 3600){
$_SESSION['check_cache'] = time();
json(1,'');
}else{
json(0,'');
}
}
}
// 检查更新
public function check()
{
// 清理目录,检查下载目录及备份目录
path_delete(RUN_PATH . '/upgrade', true);
if (! check_dir(RUN_PATH . '/upgrade', true)) {
json(0, '目录写入权限不足,无法正常升级!' . RUN_PATH . '/upgrade');
}
check_dir(DOC_PATH . STATIC_DIR . '/backup/upgrade', true);
$files = $this->getServerList();
$db = get_db_type();
foreach ($files as $key => $value) {
// 过滤掉相对路径
$value->path = preg_replace_r('{\.\.(\/|\\\\)}', '', $value->path);
$file = ROOT_PATH . $value->path;
if (@md5_file($file) != $value->md5) {
// 筛选数据库更新脚本
if (preg_match('/([\w]+)-([\w\.]+)-update\.sql/i', $file, $matches)) {
if ($matches[1] != $db || ! $this->compareVersion($matches[2], APP_VERSION . '.' . RELEASE_TIME . '.' . $this->revise)) {
continue;
}
}
if (file_exists($file)) {
$files[$key]->type = '覆盖';
$files[$key]->ltime = date('Y-m-d H:i:s', filemtime($file));
} else {
$files[$key]->type = '新增';
$files[$key]->ltime = '无';
}
$files[$key]->ctime = date('Y-m-d H:i:s', $files[$key]->ctime);
$upfile[] = $files[$key];
}
}
if (! $upfile) {
json(1, '您的系统无任何文件需要更新!');
} else {
json(1, $upfile);
}
}
// 执行下载
public function down()
{
if (! ! $list = get('list')) {
if (! is_array($list)) { // 单个文件转换为数组
$list = array(
$list
);
}
$len = count($list) ?: 0;
foreach ($list as $value) {
// 过滤掉相对路径
$value = preg_replace_r('{\.\.(\/|\\\\)}', '', $value);
// 本地存储路径
$path = RUN_PATH . '/upgrade' . $value;
// 自动创建目录
if (! check_dir(dirname($path), true)) {
json(0, '目录写入权限不足,无法下载升级文件!' . dirname($path));
}
// 定义执行下载的类型
$types = '.zip|.rar|.doc|.docx|.ppt|.pptx|.xls|.xlsx|.chm|.ttf|.otf|';
$pathinfo = explode(".", basename($path));
$ext = end($pathinfo); // 获取扩展
if (preg_match('/\.' . $ext . '\|/i', $types)) {
$result = $this->getServerDown('/release/' . $this->branch . $value, $path);
} else {
$result = $this->getServerFile($value, $path);
}
}
if ($len == 1) {
json(1, "更新文件 " . basename($value) . " 下载成功!");
} else {
json(1, "更新文件" . basename($value) . "等文件全部下载成功!");
}
} else {
json(0, '请选择要下载的文件!');
}
}
// 执行更新
public function update()
{
if ($_POST) {
if (! ! $list = post('list')) {
$list = explode(',', $list);
$backdir = date('YmdHis');
// 分离文件
foreach ($list as $value) {
// 过滤掉相对路径
$value = preg_replace_r('{\.\.(\/|\\\\)}', '', $value);
if (stripos($value, '/script/') === 0 && preg_match('/\.sql$/i', $value)) {
$sqls[] = $value;
} else {
$path = RUN_PATH . '/upgrade' . $value;
$des_path = ROOT_PATH . $value;
$back_path = DOC_PATH . STATIC_DIR . '/backup/upgrade/' . $backdir . $value;
if (! check_dir(dirname($des_path), true)) {
json(0, '目录写入权限不足,无法正常升级!' . dirname($des_path));
}
if (file_exists($des_path)) { // 文件存在时执行备份
check_dir(dirname($back_path), true);
copy($des_path, $back_path);
}
// 如果后台入口文件修改过名字,则自动适配
if (stripos($path, 'admin.php') !== false && stripos($_SERVER['SCRIPT_FILENAME'], 'admin.php') === false) {
if (file_exists($_SERVER['SCRIPT_FILENAME'])) {
$des_path = $_SERVER['SCRIPT_FILENAME'];
}
}
$files[] = array(
'sfile' => $path,
'dfile' => $des_path
);
}
}
// 更新数据库
if (isset($sqls)) {
$db = new DatabaseController();
switch (get_db_type()) {
case 'sqlite':
copy(DOC_PATH . $this->config('database.dbname'), DOC_PATH . STATIC_DIR . '/backup/sql/' . date('YmdHis') . '_' . basename($this->config('database.dbname')));
break;
case 'mysql':
$db->backupDB();
break;
}
sort($sqls); // 排序
foreach ($sqls as $value) {
$path = RUN_PATH . '/upgrade' . $value;
if (file_exists($path)) {
$sql = file_get_contents($path);
if (! $this->upsql($sql)) {
$this->log("数据库 $value 更新失败!");
json(0, "数据库" . basename($value) . " 更新失败!");
}
} else {
json(0, "数据库文件" . basename($value) . "不存在!");
}
}
}
// 替换文件
if (isset($files)) {
foreach ($files as $value) {
if (! copy($value['sfile'], $value['dfile'])) {
$this->log("文件 " . $value['dfile'] . " 更新失败!");
json(0, "文件 " . basename($value['dfile']) . " 更新失败,请重试!");
}
}
}
// 清理缓存
path_delete(RUN_PATH . '/upgrade', true);
path_delete(RUN_PATH . '/cache');
path_delete(RUN_PATH . '/complite');
path_delete(RUN_PATH . '/config');
$this->log("系统更新成功!");
json(1, '系统更新成功!');
} else {
json(0, '请选择要更新的文件!');
}
}
}
// 缓存文件
private function local()
{
$files = $this->getLoaclList(RUN_PATH . '/upgrade');
$files = json_decode(json_encode($files));
foreach ($files as $key => $value) {
$file = ROOT_PATH . $value->path;
if (file_exists($file)) {
$files[$key]->type = '覆盖';
$files[$key]->ltime = date('Y-m-d H:i:s', filemtime($file));
} else {
$files[$key]->type = '新增';
$files[$key]->ltime = '无';
}
$files[$key]->ctime = date('Y-m-d H:i:s', $files[$key]->ctime);
$upfile[] = $files[$key];
}
return $upfile;
}
// 执行更新数据库
private function upsql($sql)
{
$sql = explode(';', $sql);
$model = new Model();
foreach ($sql as $value) {
$value = trim($value);
if ($value) {
$model->amd($value);
}
}
return true;
}
// 获取列表
private function getServerList()
{
$param = array(
'version' => APP_VERSION . '.' . RELEASE_TIME . '.' . $this->revise,
'branch' => $this->branch,
'force' => $this->force,
'site' => get_http_url(),
'snuser' => $this->config('sn_user')
);
$url = $this->server . '/index.php?p=/upgrade/getlist&' . http_build_query($param);
if (! ! $rs = json_decode(get_url($url, '', '', true))) {
if ($rs->code) {
if (is_array($rs->data)) {
return $rs->data;
} else {
json(1, $rs->data);
}
} else {
json(0, $rs->data);
}
} else {
$this->log('连接更新服务器发生错误,请稍后再试!');
json(0, '连接更新服务器发生错误,请稍后再试!');
}
}
// 获取文件
private function getServerFile($source, $des)
{
$url = $this->server . '/index.php?p=/upgrade/getFile&branch=' . $this->branch;
$data['path'] = $source;
$file = basename($source);
if (! ! $rs = json_decode(get_url($url, $data, '', true))) {
if ($rs->code) {
if (! file_put_contents($des, base64_decode($rs->data))) {
$this->log("更新文件 " . $file . " 下载失败!");
json(0, "更新文件 " . $file . " 下载失败!");
} else {
return true;
}
} else {
json(0, $rs->data);
}
} else {
$this->log("更新文件 " . $file . " 获取失败!");
json(0, "更新文件 " . $file . " 获取失败!");
}
}
// 获取非文本文件
private function getServerDown($source, $des)
{
$url = $this->server . $source;
$file = basename($source);
if (($sfile = fopen($url, "rb")) && ($dfile = fopen($des, "wb"))) {
while (! feof($sfile)) {
$fwrite = fwrite($dfile, fread($sfile, 1024 * 8), 1024 * 8);
if ($fwrite === false) {
$this->log("更新文件 " . $file . " 下载失败!");
json(0, "更新文件 " . $file . " 下载失败!");
}
}
if ($sfile) {
fclose($sfile);
}
if ($dfile) {
fclose($dfile);
}
return true;
} else {
$this->log("更新文件 " . $file . " 获取失败!");
json(0, "更新文件 " . $file . " 获取失败!");
}
}
// 获取文件列表
private function getLoaclList($path)
{
$files = scandir($path);
foreach ($files as $value) {
if ($value != '.' && $value != '..') {
if (is_dir($path . '/' . $value)) {
$this->getLoaclList($path . '/' . $value);
} else {
$file = $path . '/' . $value;
// 避免中文乱码
if (! mb_check_encoding($file, 'utf-8')) {
$out_path = mb_convert_encoding($file, 'UTF-8', 'GBK');
} else {
$out_path = $file;
}
$out_path = str_replace(RUN_PATH . '/upgrade', '', $out_path);
$this->files[] = array(
'path' => $out_path,
'md5' => md5_file($file),
'ctime' => filemtime($file)
);
}
}
}
return $this->files;
}
// 比较程序本号
private function compareVersion($sv, $cv)
{
if (empty($sv) || $sv == $cv) {
return 0;
}
$sv = explode('.', $sv);
$cv = explode('.', $cv);
$len = count($sv) > count($cv) ? count($sv) : count($cv);
for ($i = 0; $i < $len; $i ++) {
$n1 = $sv[$i] or 0;
$n2 = $cv[$i] or 0;
if ($n1 > $n2) {
return 1;
} elseif ($n1 < $n2) {
return 0;
}
}
return 0;
}
}