Upgrade2.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. <?php
  2. ini_set('max_execution_time', 0);
  3. ini_set('memory_limit', '500M');
  4. class Upgrade2
  5. {
  6. protected string $archiveFile = "";//升级文件地址
  7. protected string $extractPath = "";//解压目录地址
  8. protected string $root_path = "";//程序根目录
  9. public string $update_download_url = "";//升级zip文件下载地址
  10. public string $update_sql_url = "";//升级sql脚本文件地址
  11. public string $update_script = "";//升级后执行的脚本地址
  12. protected bool $isLog = false;
  13. //构造方法初始化一些数据
  14. function __construct($update_download_url = null, $update_sql_url = null, $update_script = null)
  15. {
  16. $this->archiveFile = runtime_path() . 'mtab.zip';
  17. $this->extractPath = runtime_path();
  18. $this->root_path = root_path();
  19. if ($update_download_url !== null) {
  20. $this->update_download_url = $update_download_url;
  21. }
  22. if ($update_sql_url !== null) {
  23. $this->update_sql_url = $update_sql_url;
  24. }
  25. if ($update_script !== null) {
  26. $this->update_script = $update_script;
  27. }
  28. }
  29. //运行入口
  30. function run($cli = false): bool
  31. {
  32. if ($cli) {
  33. $this->isLog = true;
  34. }
  35. return $this->startUpgrade();
  36. }
  37. public function log($msg)
  38. {
  39. if ($this->isLog) {
  40. print_r($msg . "\n");
  41. }
  42. }
  43. //新的进程启动升级
  44. private function startUpgrade(): bool
  45. {
  46. //如果有程序代码的更新资源则更新程序代码
  47. if (strlen($this->update_download_url) > 1) {
  48. //如果有遗留的解压资源则删除
  49. $this->log("正在检查是否有旧版本的安装包,并删除。");
  50. $this->deleteDirectory("{$this->extractPath}mtab");
  51. //如果存在旧的升级包则删除
  52. $this->delZip();
  53. //下载远程更新包
  54. $this->log("正在下载升级包...");
  55. if (!$this->fileDownload()) {
  56. $this->log('资源下载失败');
  57. abort(0, '资源下载失败');
  58. }
  59. //解压升级包
  60. $this->log("正在解压升级包...");
  61. if (!$this->unzip($this->archiveFile, $this->extractPath)) {
  62. $this->delZip();
  63. abort(0, '升级资源包解压失败');
  64. }
  65. $this->log("正在更新程序...");
  66. $this->deleteDirectory(public_path() . 'dist/');//删除旧的网站文件
  67. //拷贝覆盖
  68. $this->copy();
  69. //删除下载的更新包
  70. $this->log("正在删除升级包...");
  71. $this->delZip();
  72. //更新完后的一些操作
  73. }
  74. //如果有数据库的更新资源则更新程序代码
  75. if (strlen($this->update_sql_url) > 1) {
  76. $this->log("正在更新数据库...");
  77. $this->updateSql();
  78. }
  79. if (file_exists("{$this->root_path}install.sql")) {
  80. $this->log("正在更新数据库...");
  81. $this->updateSql("{$this->root_path}install.sql");
  82. }
  83. //退出
  84. return true;
  85. }
  86. private function fileDownload(): bool
  87. {
  88. $length = 0;
  89. try {
  90. $f = fopen($this->update_download_url, 'r');
  91. $w = fopen($this->archiveFile, 'wb+');
  92. $fileSize = $this->getFileSize($this->update_download_url);
  93. do {
  94. $a = fread($f, 1024 * 64);
  95. $length += strlen($a);
  96. fwrite($w, $a);
  97. // 计算下载进度
  98. $progress = ($fileSize > 0) ? round($length / $fileSize * 100, 2) : 0;
  99. // 打印进度条,在一行内更新
  100. if ($this->isLog) {
  101. if ($progress <= 100) {
  102. $this->printProgress((int)$progress);
  103. }
  104. }
  105. } while ($a);
  106. fclose($w);
  107. fclose($f);
  108. $this->log("\n下载完成");
  109. } catch (ErrorException $e) {
  110. return false;
  111. }
  112. return true;
  113. }
  114. private function printProgress(float $progress)
  115. {
  116. try {
  117. $barLength = 50; // 进度条的总长度
  118. $completed = round($progress / 100 * $barLength); // 完成的部分
  119. $bar = str_repeat('=', $completed) . str_repeat(' ', max($barLength - $completed, 0)); // 拼接进度条
  120. echo "\r[" . $bar . "] " . $progress . "%";
  121. } catch (Exception $e) {
  122. }
  123. }
  124. private function getFileSize(string $url): int
  125. {
  126. // 使用 HEAD 请求获取文件大小
  127. $headers = get_headers($url, 1);
  128. return isset($headers['Content-Length']) ? (int)$headers['Content-Length'] : 0;
  129. }
  130. //删除升级包
  131. function delZip()
  132. {
  133. if (file_exists($this->archiveFile)) {
  134. unlink($this->archiveFile);
  135. }
  136. }
  137. //解压
  138. private function unzip($archiveFile, $extractPath): bool
  139. {
  140. $zip = new ZipArchive();
  141. if ($zip->open($archiveFile) === TRUE) {
  142. $zip->extractTo($extractPath, null);
  143. $zip->close();
  144. } else {
  145. return false;
  146. }
  147. return true;
  148. }
  149. //升级的数据库
  150. function updateSql($path = null)
  151. {
  152. if ($path) {
  153. $f = fopen($path, 'r');
  154. } else {
  155. $f = fopen($this->update_sql_url, 'r');
  156. }
  157. $sql = "";
  158. do {
  159. $sqlTmp = fread($f, 1024);
  160. $sql = $sql . $sqlTmp;
  161. } while ($sqlTmp);
  162. fclose($f);
  163. // 解析SQL文件内容并执行
  164. $sql_statements = explode(';', trim($sql));
  165. foreach ($sql_statements as $sql_statement) {
  166. if (!empty($sql_statement)) {
  167. try {
  168. \think\facade\Db::execute($sql_statement);
  169. } catch (Exception $e) {
  170. }
  171. }
  172. }
  173. }
  174. //递归删除目录
  175. function deleteDirectory($dir)
  176. {
  177. if (!is_dir($dir)) {
  178. return;
  179. }
  180. $files = scandir($dir);
  181. foreach ($files as $file) {
  182. if ($file != '.' && $file != '..') {
  183. if (is_dir("$dir/$file")) {
  184. $this->deleteDirectory("$dir/$file");
  185. } else {
  186. unlink("$dir/$file");
  187. }
  188. }
  189. }
  190. rmdir($dir);
  191. }
  192. // 递归复制目录及其内容
  193. function copyDir($source, $dest)
  194. {
  195. if (!is_dir($dest)) {
  196. mkdir($dest, 0777, true);
  197. }
  198. $files = scandir($source);
  199. foreach ($files as $file) {
  200. if ($file !== '.' && $file !== '..') {
  201. $src = $source . '/' . $file;
  202. $dst = $dest . '/' . $file;
  203. if (is_dir($src)) {
  204. $this->copyDir($src, $dst);
  205. } else {
  206. copy($src, $dst);
  207. }
  208. }
  209. }
  210. }
  211. //覆盖原来的程序
  212. private function copy()
  213. {
  214. //移动覆盖
  215. $this->copyDir("{$this->extractPath}mtab/", "{$this->root_path}");
  216. //删除解压目录
  217. $this->deleteDirectory("{$this->extractPath}mtab");
  218. }
  219. }