解放双手,不想用ftp和版本控制软件,自己用php做一个在线更新网站功能,用版本号作对比下用于在后台直接上传zip包后c
no
yes
no
yes
备份完成
更新完成检测
验证token
检测是否有更新?
暂无更新
退出
下载新包
需要备份?
删除旧包文件
执行备份函数
解压新包
更新本地版本号
下用于在后台直接上传zip包后curd请求一次自动更新,这样安装包的管理能力更强,适用多个平台、多种环境的更新,就是偷懒不想一个一个平台登录ftp或者其它方式更新(自己用git、svn另说),在平台管理端可以个性的还原某个版本。
<?php
define('APP_UPDATE_DEBUG', true);//是否调试
if (APP_UPDATE_DEBUG) {
error_reporting(E_ALL);//显示所有错误
ini_set('display_errors', 'on');
} else {
error_reporting(0);//关闭所有错误
ini_set('display_errors', 'off');
}
(new AppUpdate)->run();//执行
class AppUpdate
{
const APP_UPDATE_API = 'https://abc.com/app_version';//更新接口
const APP_UPDATE_TYPE = 'h5';//更新平台
const APP_UPDATE_SECRET = 'jx5HTGJiNq8U2BmzYwKbRoLtyd6Drp7C';//密钥
const APP_UPDATE_TOKEN_TYPE = 'md5';//token验证方式
const APP_UPDATE_DELETE_DIR = ['/static/', '/uni_modules/'];//删除目录
const APP_UPDATE_NEED_BACKUP = false;//是否备份
const APP_UPDATE_BACKUP_DIR = '/backup/';//备份目录
const APP_UPDATE_UNNEED_FILE = ['.', '..', '.user.ini', '.idea', '.well-known', '.htaccess', '.nginx.htaccess', 'version'];//不用压缩的文件
//验证token
private static function check_token()
{
$token = !empty($_REQUEST['token']) ? $_REQUEST['token'] : '';
if (!$token) {
self::run_exit('token is empty');
}
//自定义验证token
$method = self::APP_UPDATE_TOKEN_TYPE;
if ($token != $method(self::APP_UPDATE_SECRET)) {
self::run_exit('token is error');
}
}
//执行
public function run()
{
self::check_token();
$version = (int)@file_get_contents(__DIR__ . '/version');
$version = $version > 0 ? $version : 100;
//更新检测接口
$url = self::APP_UPDATE_API . '?type=' . self::APP_UPDATE_TYPE . '&version=' . $version;
$res = $this->get($url);
if (empty($res) || $res['status'] == 200) {
self::run_exit('没有更新');
}
$info = $res['data'];
if ($info['version'] <= $version || empty($info['url'])) {
self::run_exit('无须更新');
}
//下载更新包
$fileName = __DIR__ . '/new' . $info['version'] . '.zip';
$file = $this->remote2local($info['url'], $fileName);
if (!file_exists($fileName)) {
self::run_exit('下载失败');
}
//执行备份
if (self::APP_UPDATE_NEED_BACKUP) {
$this->backup();
}
// 要删除目录
if (!empty(self::APP_UPDATE_DELETE_DIR)) {
foreach (self::APP_UPDATE_DELETE_DIR as $dir) {
$this->deleteDirectory(__DIR__ . $dir);
}
}
$zip = new \ZipArchive ();
$zip->open($fileName);
$decode = $zip->extractTo(__DIR__ . '/');
$zip->close();
if (!$decode) {
self::run_exit('解压失败');
}
//更新版本号
@file_put_contents(__DIR__ . '/version', $info['version']);
@unlink($fileName);//删除压缩包
self::run_exit('升级成功');
}
//输出
private static function run_exit($msg)
{
exit($msg);
}
//获取远程文件
private function get($url)
{
$headerArray = ["Content-type:application/json;", "Accept:application/json"];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
if (APP_UPDATE_DEBUG) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headerArray);
$output = curl_exec($ch);
if (curl_errno($ch)) {
self:: run_exit(curl_error($ch));
}
curl_close($ch);
if (stripos($output, '{') === 0) {
return json_decode($output, true);
}
return $output;
}
//删除目录
private function deleteDirectory($directoryPath)
{
if (!is_dir($directoryPath)) {
return;
}
$iterator = new \RecursiveDirectoryIterator($directoryPath, \RecursiveDirectoryIterator::SKIP_DOTS);
$iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST);
foreach ($iterator as $file) {
$file->isFile() ? unlink($file->getRealPath()) : rmdir($file->getRealPath());
}
rmdir($directoryPath);
}
//备份旧文件
private function backup()
{
$dir = __DIR__ . self::APP_UPDATE_BACKUP_DIR;
if (!is_dir($dir)) {
@mkdir($dir, 0777, true);
}
if (!is_writeable($dir)) {
self::run_exit('备份目录没有写入权限');
}
$zipName = $dir . date('YmdHi') . '.zip';
$zip = new \ZipArchive ();
$zip->open($zipName, \ZipArchive::CREATE);
$this->createZip(opendir($dir), $zip, $dir);
$zip->close();
if (!file_exists($zipName)) {
self::run_exit('备份失败');
}
}
//远程文件转本地文件
private function remote2local($url, $localPath)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//开发环境取消ssl认证
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$res = curl_exec($ch);
if (curl_errno($ch)) {
return false;
}
curl_close($ch);
//保存到本地
$file = fopen($localPath, 'w');
fwrite($file, $res);
fclose($file);
return $localPath;
}
//递归创建压缩包
private function createZip($openFile, $zipObj, $sourcePath, $newRelat = '')
{
while (($file = readdir($openFile)) != false) {
if (in_array($file, self::APP_UPDATE_UNNEED_FILE)) {
continue;
}
//跳过压缩包,主要是备份的文件
if (substr($file, -4) == '.zip') continue;
/*源目录路径(绝对路径)*/
$sourceTemp = $sourcePath . '/' . $file;
/*目标目录路径(相对路径)*/
$newTemp = $newRelat == '' ? $file : $newRelat . '/' . $file;
if (is_dir($sourceTemp)) {
$zipObj->addEmptyDir($newTemp);
$this->createZip(opendir($sourceTemp), $zipObj, $sourceTemp, $newTemp);
}
if (is_file($sourceTemp)) {
$zipObj->addFile($sourceTemp, $newTemp);
}
}
}
}
未经测试,请勿用于生产环境,谢谢。 要php7以上才行哦,自己做好user.ini控制,让php能够执行指定目录,也可以通过nginx反向代理执行某个php文件。
转载自:https://juejin.cn/post/7418118194341937171