SaiUpgrade.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. <?php
  2. namespace plugin\saiadmin\command;
  3. use Symfony\Component\Console\Command\Command;
  4. use Symfony\Component\Console\Input\InputInterface;
  5. use Symfony\Component\Console\Output\OutputInterface;
  6. use Symfony\Component\Console\Style\SymfonyStyle;
  7. /**
  8. * SaiAdmin 升级命令
  9. * 用于从 vendor 目录升级 saiadmin 插件到最新版本
  10. */
  11. class SaiUpgrade extends Command
  12. {
  13. protected static $defaultName = 'sai:upgrade';
  14. protected static $defaultDescription = '升级 SaiAdmin 插件到最新版本';
  15. /**
  16. * 升级源目录
  17. */
  18. protected string $sourcePath;
  19. /**
  20. * 目标插件目录
  21. */
  22. protected string $targetPath;
  23. protected function configure(): void
  24. {
  25. $this->setName('sai:upgrade')
  26. ->setDescription('升级 SaiAdmin 插件到最新版本');
  27. }
  28. protected function execute(InputInterface $input, OutputInterface $output): int
  29. {
  30. $io = new SymfonyStyle($input, $output);
  31. $io->title('SaiAdmin 升级工具');
  32. $io->text([
  33. '此命令将从 vendor 目录复制最新版本的 saiadmin 插件文件到 plugin 目录',
  34. '源目录: vendor/saithink/saiadmin/src/plugin/saiadmin',
  35. '目标目录: plugin/saiadmin',
  36. ]);
  37. $io->newLine();
  38. // 设置路径
  39. $this->sourcePath = BASE_PATH . '/vendor/saithink/saiadmin/src/plugin/saiadmin';
  40. $this->targetPath = BASE_PATH . '/plugin/saiadmin';
  41. // 检查源目录是否存在
  42. if (!is_dir($this->sourcePath)) {
  43. $io->error([
  44. "升级源目录不存在: {$this->sourcePath}",
  45. "请确保已通过 composer 安装了 saithink/saiadmin 包",
  46. ]);
  47. return Command::FAILURE;
  48. }
  49. // 获取版本信息
  50. $currentVersion = $this->getVersion($this->targetPath);
  51. $latestVersion = $this->getVersion($this->sourcePath);
  52. // 显示版本信息
  53. $io->section('版本信息');
  54. $io->table(
  55. ['项目', '版本'],
  56. [
  57. ['当前版本', $currentVersion ?: '未知'],
  58. ['最新版本', $latestVersion ?: '未知'],
  59. ]
  60. );
  61. // 版本对比提示
  62. if ($currentVersion && $latestVersion) {
  63. if (version_compare($currentVersion, $latestVersion, '>=')) {
  64. $io->success('当前已是最新版本!');
  65. if (!$io->confirm('是否仍要继续覆盖安装?', false)) {
  66. $io->info('操作已取消');
  67. return Command::SUCCESS;
  68. }
  69. } else {
  70. $io->info("发现新版本: {$currentVersion} → {$latestVersion}");
  71. }
  72. }
  73. // 警告信息
  74. $io->warning([
  75. "注意:此操作将覆盖 {$this->targetPath} 目录的现有文件!",
  76. "建议在执行前备份您的自定义修改。",
  77. ]);
  78. // 确认操作
  79. if (!$io->confirm('确定要执行升级操作吗?', false)) {
  80. $io->info('操作已取消');
  81. return Command::SUCCESS;
  82. }
  83. $io->section('开始升级...');
  84. try {
  85. $copiedFiles = $this->copyDirectory($this->sourcePath, $this->targetPath, $io);
  86. $io->newLine();
  87. $io->success([
  88. "SaiAdmin 升级成功!",
  89. "复制文件数: {$copiedFiles}",
  90. ]);
  91. $io->note([
  92. '请重启 webman 服务使更改生效',
  93. '命令: php webman restart 或 php windows.php',
  94. ]);
  95. return Command::SUCCESS;
  96. } catch (\Exception $e) {
  97. $io->error("升级失败: " . $e->getMessage());
  98. return Command::FAILURE;
  99. }
  100. }
  101. /**
  102. * 递归复制目录
  103. * @param string $source 源目录
  104. * @param string $dest 目标目录
  105. * @param SymfonyStyle $io 输出接口
  106. * @return int 复制的文件数量
  107. */
  108. protected function copyDirectory(string $source, string $dest, SymfonyStyle $io): int
  109. {
  110. $count = 0;
  111. if (!is_dir($dest)) {
  112. mkdir($dest, 0755, true);
  113. }
  114. $iterator = new \RecursiveIteratorIterator(
  115. new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS),
  116. \RecursiveIteratorIterator::SELF_FIRST
  117. );
  118. // 先计算文件总数用于进度条
  119. $files = [];
  120. foreach ($iterator as $item) {
  121. if (!$item->isDir()) {
  122. $files[] = $item;
  123. }
  124. }
  125. // 创建进度条
  126. $io->progressStart(count($files));
  127. // 重新遍历并复制
  128. $iterator = new \RecursiveIteratorIterator(
  129. new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS),
  130. \RecursiveIteratorIterator::SELF_FIRST
  131. );
  132. foreach ($iterator as $item) {
  133. $destPath = $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName();
  134. if ($item->isDir()) {
  135. if (!is_dir($destPath)) {
  136. mkdir($destPath, 0755, true);
  137. }
  138. } else {
  139. copy($item->getPathname(), $destPath);
  140. $count++;
  141. $io->progressAdvance();
  142. }
  143. }
  144. $io->progressFinish();
  145. return $count;
  146. }
  147. /**
  148. * 从目录中获取版本号
  149. * @param string $path 插件目录路径
  150. * @return string|null 版本号
  151. */
  152. protected function getVersion(string $path): ?string
  153. {
  154. $configFile = $path . '/config/app.php';
  155. if (!file_exists($configFile)) {
  156. return null;
  157. }
  158. try {
  159. $config = include $configFile;
  160. return $config['version'] ?? null;
  161. } catch (\Exception $e) {
  162. return null;
  163. }
  164. }
  165. }