TaoLer/app/admin/controller/Upgrade.php
2020-01-08 17:37:41 +08:00

495 lines
13 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
/**
* 升级包规定的目录结构
* xxx_版本号.zip(如xxx_1.0.0.zip)
* |
* |————mysql
* | |
* | |___mysql_update.sql(更新脚本) //create table test(id init(11)); create table test2(id init(11));
*
* | |___mysql_rockback.sql回滚脚本 //drop table test; //drop table test2;
* |
* |____php
*
*/
namespace app\admin\controller;
use app\common\controller\AdminController;
use think\facade\View;
use think\facade\Request;
use think\facade\Db;
use think\exception\ValidateException;
use app\admin\controller\Uzip;
use think\facade\Cache;
class Upgrade extends AdminController
{
public $update_log = "../runtime/update/update_log.log"; //系统升级日志
public $return_log = "../runtime/update/return_log.log"; //系统回滚日志
public $progress_log = "../runtime/update/progress_log.log"; //记录进度
public $root_dir = "../"; //站点代码的根目录
public $backup_dir = "../runtime/update/backup_dir/"; //备份目录
public $upload_dir = "../runtime/update/upload_dir/"; //升级包目录
public $sys_version_num; //当前系统的版本
public function __construct()
{
$this->sys_version_num = Db::name('system')->where('id',1)->value('sys_version_num');
}
/** 展示升级界面 */
public function index()
{
$sys_version_num = Db::name('system')->field('sys_version_num,key,upcheck_url,upgrade_url')->find(1);
View::assign('ver_num',$sys_version_num);
return View::fetch();
}
//设置key
public function key()
{
$data = Request::param();
if($data['key']== ''){
return json(['code'=>0,'msg'=>'请填写正确的key']);
}
$res = Db::name('system')->update(['key'=>$data['key'],'id'=>1]);
if($res){
$res = ['code'=>1,'msg'=>'保存成功'];
} else {
$res = ['code'=>0,'msg'=>'保存失败'];
}
return json($res);
}
//修改key
public function keyedit()
{
$key = Db::name('system')->field('key,upcheck_url,upgrade_url')->find(1);
if(Request::isAjax()){
$data = Request::param();
if($data['key']== ''){
return json(['code'=>0,'msg'=>'请正确填写申请到的key']);
}
$res = Db::name('system')->update(['key'=>$data['key'],'upcheck_url'=>$data['upcheck_url'],'upgrade_url'=>$data['upgrade_url'],'id'=>1]);
if($res){
$res = ['code'=>1,'msg'=>'修改成功'];
} else {
$res = ['code'=>0,'msg'=>'修改失败'];
}
return json($res);
}
View::assign('key',$key);
return View::fetch();
}
//升级前的版本检测
public function check($url)
{
$url = $url.'?ver='.$this->sys_version_num;
$ch =curl_init ();
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT, 20);
curl_setopt($ch,CURLOPT_POST, 1);
$data = curl_exec($ch);
$httpCode = curl_getinfo($ch,CURLINFO_HTTP_CODE);
curl_close($ch);
if($httpCode != '200'){
return json(['code'=>0,'msg'=>'连接服务器失败,稍后重试...']);
}
$versions = json_decode($data);
//判断状态
$version_code = $versions->code;
if($version_code == 0){
return json(['code'=>$version_code,'msg'=>$versions->msg]);
}
//版本比较
$version_num = $versions->version;
$up_num =$versions->up_num;
$res = version_compare($version_num,$this->sys_version_num,'>');
if($res){
return json(['code'=>1,'msg'=>'发现新版本','version'=>$version_num,'upnum'=>$up_num]);
} else {
return json(['code'=>0,'msg'=>'暂时还没更新哦! ==8']);
}
}
/**
* 自动更新
*/
public function upload()
{
$data = Request::param();
if(empty($data['key'])){
return json(["code"=>0,"msg"=>"请配置正确升级key"]);
}
$url = $data['url'].'?domain='.Request::domain().'&key='.$data['key'].'&ver='.$this->sys_version_num;
$ch =curl_init ();
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT, 20);
curl_setopt($ch,CURLOPT_POST, 1);
$data = curl_exec($ch);
$httpCode = curl_getinfo($ch,CURLINFO_HTTP_CODE);
curl_close($ch);
if($httpCode != '200'){
return json(['code'=>0,'msg'=>'连接服务器失败,稍后重试...']);
}
//获取远程文件
$versions = json_decode($data);
//判断状态
$version_code = $versions->code;
if($version_code == 0){
return json(['code'=>$version_code,'msg'=>$versions->msg]);
}
$version_num = $versions->version;
$file_url = $versions->src;
//判断远程文件是否可用存在
$header = get_headers($file_url, true);
if(!isset($header[0]) && (strpos($header[0], '200') || strpos($header[0], '304'))){
return json(["code"=>0,"msg"=>"获取远程文件失败"]);
}
if(!is_dir($this->upload_dir)){
$this->create_dirs($this->upload_dir);
}
//把远程文件放入本地
$package_file = $this->upload_dir.'tao_'.$version_num.'.zip';
$cpfile = copy($file_url,$package_file);
if(!$cpfile)
{
return json(["code"=>0,"msg"=>"下载升级文件失败"]);
}
//记录下日志
$this->save_log("上传升级包成功!");
$this->update_progress("20%");
//升级前备份代码
$ex = array('app','view');
$backup_code_res = $this->copydir('../', $this->backup_dir, $ex);
if(!$backup_code_res){
$this->save_log("备份失败!");
return json(["code"=>0,"msg"=>"备份失败"]);
}
$this->update_progress("30%");
//执行升级
$upres = $this->execute_update($package_file);
//更新版本
Db::name('system')->update(['sys_version_num'=>$version_num,'id'=>1]);
if($upres){
return json(["code"=>1,"msg"=>"升级成功"]);
}else {
return json(["code"=>0,"msg"=>"升级失败"]);
}
}
/**
* 处理升级包上传
*/
public function uploadZip()
{
$files = request()->file('file');
if($files)
{
$name = $files->getOriginalName();
if(!$name)
{
return json(["code"=>0,"msg"=>"请上传升级包文件"]);
}
}
//校验后缀
$astr = explode('.',$name);
$ext = array_pop($astr);
if($ext != 'zip')
{
return json(["code"=>0,"msg"=>"请上传文件格式不对"]);
}
//对比版本号
$astr = explode('_',$name);
$version_num = str_replace(".zip", '',array_pop($astr));
//var_dump($version_num);
if(!$version_num)
{
return json(["code"=>0,"msg"=>"获取版本号失败"]);
}
//对比
if(!$this->compare_version($version_num))
{
return json(["code"=>0,"msg"=>"版本升级不能降级!请检查..."]);
}
if(!is_dir($this->upload_dir)){
$this->create_dirs($this->upload_dir);
}
$package_file = $this->upload_dir.$name;
//$mv = $files->move('/../tmp/web/upload_dir',$version_num);
$mfile = move_uploaded_file($files,$package_file);
if(!$mfile)
{
return json(["code"=>0,"msg"=>"上传文件失败"]);
}
//记录下日志
$this->save_log("上传升级包成功!");
$this->update_progress("20%");
//升级前备份代码
$ex = array('app','view');
$backup_code_res = $this->copydir('../', $this->backup_dir, $ex);
if(!$backup_code_res){
$this->save_log("备份失败!");
return json(["code"=>0,"msg"=>"备份失败"]);
}
$this->update_progress("30%");
//执行升级
$upres = $this->execute_update($package_file);
//更新版本
Db::name('system')->update(['sys_version_num'=>$version_num,'id'=>1]);
if($upres){
return json(["code"=>1,"msg"=>"升级成功"]);
}else {
return json(["code"=>0,"msg"=>"升级失败"]);
}
}
/**
* 升级操作
* @return [type] [description]
*/
private function execute_update($package_file)
{
//解压 如何使用zip加密压缩这里解压缩的时候注意要解密
$uzip = new Uzip();
$unzip_res = $uzip->uzip($package_file,$this->upload_dir,true);
if(!$unzip_res)
{
$this->save_log("解压失败");
return json(["code"=>0,"msg"=>"解压失败"]);
}
//解压成功,得到文件夹
$package_name = str_replace(".zip","",$package_file);
//var_dump($package_name);
$this->update_progress("50%");
/*
//升级mysql
if(file_exists($this->upload_dir.'/'.$package_file."/mysql/mysql_update.sql"))
{
$result = $this->database_operation($this->upload_dir.'/'.$package_file."/mysql/mysql_update.sql");
if(!$result['code'])
{
echo json($result);die;
}
}
*/
$this->update_progress("70%");
if(is_dir($package_name.'/'))
{
//升级PHP
$cp_res = $this->copydir($package_name.'/', $this->root_dir);
if(!$cp_res)
{
$this->save_log("php更新失败");
//数据库回滚
if(file_exists($this->upload_dir.'/'.$package_file."/mysql/mysql_rockback.sql"))
{
$this->save_log("数据库回滚");
$this->database_operation($this->upload_dir.'/'.$package_file."/mysql/mysql_rockback.sql");
}
//php代码回滚 升级前备份的代码
$backup_code_res = $this->copydir($this->backup_dir, $this->zip_dir);
if($backup_code_res){
$this->save_log("php回滚");
return json(["code"=>0,"msg"=>"php更新失败"]);
}
}
}
//把解压的升级包清除
//$del_zip = unlink($package_file);
$this->delDirAndFile($this->upload_dir);
$this->delDirAndFile($this->backup_dir);
$this->update_progress("100%");
//更新系统的版本号了
//更新php的版本号了(应该跟svngit的版本号一致)
//更新数据库的版本号了(应该跟svngit的版本号一致)
return true;
}
/**
* 比较代码版本
* @return [type] [description]
*/
private function compare_version($version_num='1.0.0')
{
return version_compare($version_num,$this->sys_version_num,'>');
//return json(['code'=>1,'msg'=>'版本','data'=>$version]);
}
/**
* 数据库操作
*/
public function database_operation($file)
{
$mysqli = new mysqli("localhost","root","root","test");
if($mysqli->connect_errno)
{
return json(["code"=>0,"msg"=>"Connect failed:".$mysqli->connect_error]);
}
$sql = file_get_contents($file);
$a = $mysqli->multi_query($sql);
return ["code"=>1,"msg"=>"数据库操作OK"];
}
/**
* 返回系统升级的进度
*/
public function update_progress($progress)
{
exec(" echo '".$progress."' > $this->progress_log ");
}
/**
* 记录日志
*/
public function save_log($msg,$action="update")
{
$msg .= date("Y-m-d H:i:s").":".$msg."\n";
if($action == "update")
{
exec(" echo '".$msg."' >> $this->update_log ");
}else
{
exec(" echo '".$msg."' >> $this->return_log ");
}
}
/**
* 复制文件夹$source下的文件和子文件夹下的内容到$dest下 升级+备份代码
* @param $source
* @param $dest
* @param $ex 定义指定复制的目录,默认全复制
*/
public function copydir($source, $dest, $ex=array())
{
if (!file_exists($dest)) mkdir($dest);
if($handle = opendir($source)){
while (($file = readdir($handle)) !== false) {
if (( $file != '.' ) && ( $file != '..' )) {
if ( is_dir($source . $file) ) {
if(count($ex) != 0){
if(in_array($file,$ex)){
$this->copydir($source . $file.'/', $dest . $file.'/');
}
}else{
$this->copydir($source . $file.'/', $dest . $file.'/');
}
} else {
copy($source. $file, $dest . $file);
}
}
}
closedir($handle);
} else {
return false;
}
return true;
}
/**
* 删除文件夹及内容
* @param $dirPath
* @param $nowDir 是否删除当前文件夹$dirPath true false
*/
public function delDirAndFile( $dirPath, $nowDir=false )
{
if ( $handle = opendir($dirPath) ) {
while ( false !== ( $item = readdir( $handle ) ) ) {
if ( $item != '.' && $item != '..' ) {
$path = $dirPath.$item;
//var_dump($path);
if (is_dir($path)) {
$this->delDirAndFile($path.'/');
rmdir($path.'/');
} else {
unlink($path);
}
}
}
closedir( $handle );
/*
if($nowDir == true){
if(!rmdir($dirPath)){
return false;
}
}
*/
} else {
return false;
}
return true;
}
//创建多文件夹
public function create_dirs($path)
{
if (!is_dir($path))
{
$directory_path = "";
$directories = explode("/",$path);
array_pop($directories);
foreach($directories as $directory)
{
$directory_path .= $directory."/";
if (!is_dir($directory_path))
{
mkdir($directory_path);
chmod($directory_path, 0777);
}
}
}
}
}