MigrateEnvFile.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. <?php
  2. /**
  3. * 迁移 .env 文件
  4. *
  5. * @author mybsdc <[email protected]>
  6. * @date 2021/11/3
  7. * @time 15:57
  8. */
  9. namespace Luolongfei\App\Console;
  10. use Luolongfei\Libs\Env;
  11. class MigrateEnvFile extends Base
  12. {
  13. /**
  14. * @var array 当前已有的环境变量数据
  15. */
  16. protected $allOldEnvValues;
  17. /**
  18. * @var int 迁移环境变量数量
  19. */
  20. public $migrateNum = 0;
  21. /**
  22. * @var MigrateEnvFile
  23. */
  24. private static $instance;
  25. /**
  26. * @return MigrateEnvFile
  27. */
  28. public static function getInstance()
  29. {
  30. if (!self::$instance instanceof self) {
  31. self::$instance = new self();
  32. }
  33. return self::$instance;
  34. }
  35. private function __construct()
  36. {
  37. $this->allOldEnvValues = $this->getAllOldEnvValues();
  38. }
  39. private function __clone()
  40. {
  41. }
  42. /**
  43. * 获取当前有效的旧的环境变量值
  44. *
  45. * 会做一些基本的处理,让旧版数据兼容新版 .env 文件
  46. *
  47. * @return array
  48. */
  49. protected function getAllOldEnvValues()
  50. {
  51. $allOldEnvValues = env();
  52. unset($allOldEnvValues['ENV_FILE_VERSION']);
  53. $allOldEnvValues = array_filter($allOldEnvValues, function ($val) {
  54. return $val !== '';
  55. });
  56. $allOldEnvValues = array_map(function ($val) {
  57. $tmpVal = strtolower($val);
  58. if ($tmpVal === 'true' || $tmpVal === true) {
  59. return 1;
  60. } else if ($tmpVal === 'false' || $tmpVal === false) {
  61. return 0;
  62. } else {
  63. return $val;
  64. }
  65. }, $allOldEnvValues);
  66. return $allOldEnvValues;
  67. }
  68. /**
  69. * 是否需要迁移
  70. *
  71. * @return bool
  72. * @throws \Exception
  73. */
  74. public function needToMigrate()
  75. {
  76. $envVer = $this->getEnvFileVer();
  77. if (is_null($envVer)) {
  78. return true;
  79. }
  80. $envExampleVer = $this->getEnvFileVer('.env.example');
  81. return version_compare($envExampleVer, $envVer, '>');
  82. }
  83. public function getEnvFilePath($filename = '.env')
  84. {
  85. return ROOT_PATH . DS . $filename;
  86. }
  87. /**
  88. * 获取 env 文件版本
  89. *
  90. * @param string $filename
  91. *
  92. * @return string|null
  93. * @throws \Exception
  94. */
  95. public function getEnvFileVer($filename = '.env')
  96. {
  97. $file = $this->getEnvFilePath($filename);
  98. if (!file_exists($file)) {
  99. throw new \Exception('文件不存在:' . $file);
  100. }
  101. if (($fileContent = file_get_contents($file)) === false) {
  102. throw new \Exception('读取文件内容失败:' . $file);
  103. }
  104. if (!preg_match('/^ENV_FILE_VERSION=(?P<env_file_version>.*?)$/im', $fileContent, $m)) {
  105. return null;
  106. }
  107. return $this->getVerNum($m['env_file_version']);
  108. }
  109. /**
  110. * 备份旧文件
  111. *
  112. * 如果目标文件已存在,将会被覆盖
  113. *
  114. * @return bool
  115. * @throws \Exception
  116. */
  117. public function backup()
  118. {
  119. if (copy($this->getEnvFilePath(), $this->getEnvFilePath('.env.old')) === false) {
  120. throw new \Exception('备份 .env 文件到 .env.old 文件时出错');
  121. }
  122. return true;
  123. }
  124. /**
  125. * 生成新的 .env 文件
  126. *
  127. * @return bool
  128. * @throws \Exception
  129. */
  130. public function genNewEnvFile()
  131. {
  132. if (copy($this->getEnvFilePath('.env.example'), $this->getEnvFilePath('.env')) === false) {
  133. throw new \Exception('从 .env.example 文件生成 .env 文件时出错');
  134. }
  135. return true;
  136. }
  137. /**
  138. * 迁移环境变量数据
  139. *
  140. * @param array $allEnvVars
  141. * @throws \Exception
  142. */
  143. public function migrateData(array $allEnvVars)
  144. {
  145. foreach ($allEnvVars as $envKey => $envVal) {
  146. if ($this->setEnv($envKey, $envVal)) {
  147. $this->migrateNum++;
  148. }
  149. }
  150. // 重载环境变量
  151. Env::getInstance()->init('.env', true);
  152. }
  153. /**
  154. * 写入单个环境变量值
  155. *
  156. * @param string $key
  157. * @param $value
  158. *
  159. * @return bool
  160. */
  161. public function setEnv(string $key, $value)
  162. {
  163. $envFilePath = $this->getEnvFilePath();
  164. $contents = file_get_contents($envFilePath);
  165. $contents = preg_replace("/^{$key}=[^\r\n]*/miu", $this->formatEnvVal($key, $value), $contents, -1, $count);
  166. return $this->writeFile($envFilePath, $contents) && $count;
  167. }
  168. /**
  169. * 格式化环境变量
  170. *
  171. * @param string $key
  172. * @param string|integer $value
  173. *
  174. * @return string
  175. */
  176. public function formatEnvVal($key, $value)
  177. {
  178. return sprintf(is_numeric($value) ? '%s=%s' : "%s='%s'", $key, $value);
  179. }
  180. /**
  181. * 覆盖文件内容
  182. *
  183. * @param string $path
  184. * @param string $contents
  185. *
  186. * @return bool
  187. */
  188. protected function writeFile(string $path, string $contents): bool
  189. {
  190. $file = fopen($path, 'w');
  191. fwrite($file, $contents);
  192. return fclose($file);
  193. }
  194. /**
  195. * @return bool
  196. */
  197. public function handle()
  198. {
  199. try {
  200. if (!$this->needToMigrate()) {
  201. return true;
  202. }
  203. system_log('检测到你的 .env 文件内容过旧,程式将根据 .env.example 文件自动更新相关配置项,不要慌张,此操作对已有数据不会有任何影响');
  204. $this->backup();
  205. system_log(sprintf('<green>已完成 .env 文件备份</green>,旧文件位置为 %s/.env.old', ROOT_PATH));
  206. $this->genNewEnvFile();
  207. system_log('已生成新 .env 文件');
  208. $this->migrateData($this->allOldEnvValues);
  209. system_log(sprintf('<green>数据迁移完成</green>,共迁移 %d 条环境变量数据', $this->migrateNum));
  210. system_log('<green>恭喜,已成功完成 .env 文件升级</green>');
  211. return true;
  212. } catch (\Exception $e) {
  213. system_log('升级 .env 文件出错:' . $e->getMessage());
  214. return false;
  215. }
  216. }
  217. }