MigrateEnvFile.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  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(lang('100021') . $file);
  100. }
  101. if (($fileContent = file_get_contents($file)) === false) {
  102. throw new \Exception(lang('100022') . $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(lang('100020'));
  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(lang('100019'));
  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. // 强行覆盖原有的最大请求重试次数
  147. if ($envKey === 'MAX_REQUEST_RETRY_COUNT') {
  148. continue;
  149. }
  150. if ($this->setEnv($envKey, $envVal)) {
  151. $this->migrateNum++;
  152. }
  153. }
  154. // 重载环境变量
  155. Env::getInstance()->init('.env', true);
  156. }
  157. /**
  158. * 写入单个环境变量值
  159. *
  160. * @param string $key
  161. * @param $value
  162. *
  163. * @return bool
  164. */
  165. public function setEnv(string $key, $value)
  166. {
  167. $envFilePath = $this->getEnvFilePath();
  168. $contents = file_get_contents($envFilePath);
  169. $contents = preg_replace("/^{$key}=[^\r\n]*/miu", $this->formatEnvVal($key, $value), $contents, -1, $count);
  170. return $this->writeFile($envFilePath, $contents) && $count;
  171. }
  172. /**
  173. * 格式化环境变量
  174. *
  175. * @param string $key
  176. * @param string|integer $value
  177. *
  178. * @return string
  179. */
  180. public function formatEnvVal($key, $value)
  181. {
  182. return sprintf(is_numeric($value) ? '%s=%s' : "%s='%s'", $key, $value);
  183. }
  184. /**
  185. * 覆盖文件内容
  186. *
  187. * @param string $path
  188. * @param string $contents
  189. *
  190. * @return bool
  191. */
  192. protected function writeFile(string $path, string $contents): bool
  193. {
  194. $file = fopen($path, 'w');
  195. fwrite($file, $contents);
  196. return fclose($file);
  197. }
  198. /**
  199. * @return bool
  200. */
  201. public function handle()
  202. {
  203. try {
  204. if (!$this->needToMigrate()) {
  205. return true;
  206. }
  207. system_log(lang('100013'));
  208. $this->backup();
  209. system_log(sprintf(lang('100014'), ROOT_PATH));
  210. $this->genNewEnvFile();
  211. system_log(lang('100015'));
  212. $this->migrateData($this->allOldEnvValues);
  213. system_log(sprintf(lang('100016'), $this->migrateNum));
  214. system_log(lang('100017'));
  215. return true;
  216. } catch (\Exception $e) {
  217. system_log(lang('100018') . $e->getMessage());
  218. return false;
  219. }
  220. }
  221. }