فهرست منبع

自动升级 .env 文件并迁移相关数据

luolongfei 4 سال پیش
والد
کامیت
5cc3eb8937
9فایلهای تغییر یافته به همراه344 افزوده شده و 29 حذف شده
  1. 7 4
      .env.example
  2. 14 0
      app/Console/Base.php
  3. 20 18
      app/Console/FreeNom.php
  4. 278 0
      app/Console/MigrationEnvFile.php
  5. 5 1
      app/helpers.php
  6. 8 2
      index.php
  7. 5 3
      libs/Env.php
  8. 1 1
      resources/lang/zh.php
  9. 6 0
      run

+ 7 - 4
.env.example

@@ -11,6 +11,9 @@
 # - 单账户和多账户的配置会被合并在一起读取并去重
 #####################################################################
 
+# .env 文件版本
+ENV_FILE_VERSION='v1'
+
 ######################  账户配置 Account config  #########################
 # Freenom账户 Freenom Account
 [email protected]
@@ -36,7 +39,7 @@ TO=''
 MAIL_ENABLE=1
 
 # 自定义邮箱配置 Custom email config
-# 如果你想使用除“QQ邮箱、163邮箱、Gmail、Outlook邮箱”外的第三方邮箱或者自建邮箱服务作为机器人邮箱,可以自定义邮箱配置,否则请不要配置这些项
+# 如果你想使用除 “QQ邮箱、163邮箱、Gmail、Outlook邮箱” 外的第三方邮箱或者自建邮箱服务作为机器人邮箱,可以自定义邮箱配置,否则请不要配置这些项
 # If you want to use third-party mailboxes or self-built mailbox services other than "QQ mailbox, 163 mailbox, Gmail, Outlook mailbox" as robot mailbox, you can customize mailbox configuration, otherwise, please don't configure these items.
 
 # SMTP 服务器 SMTP server
@@ -61,11 +64,11 @@ TELEGRAM_CHAT_ID=''
 # 你的Telegram bot的token Token for your Telegram bot
 TELEGRAM_BOT_TOKEN=''
 
-# 是否启用 Telegram Bot 功能 1:启用 0:不启用 Whether to enable Telegram Bot features 1: enabled 0: not enabled
-TELEGRAM_BOT_ENABLE=0
-
 # Telegram 代理 e.g. http://127.0.0.1:1081
 TELEGRAM_PROXY=''
+
+# 是否启用 Telegram Bot 功能 1:启用 0:不启用 Whether to enable Telegram Bot features 1: enabled 0: not enabled
+TELEGRAM_BOT_ENABLE=0
 ######################  end Telegram bot  #########################
 
 ######################  企业微信  #########################

+ 14 - 0
app/Console/Base.php

@@ -0,0 +1,14 @@
+<?php
+/**
+ * 基类
+ *
+ * @author mybsdc <[email protected]>
+ * @date 2021/11/3
+ * @time 16:32
+ */
+
+namespace Luolongfei\App\Console;
+
+class Base
+{
+}

+ 20 - 18
app/Console/FreeNom.php

@@ -15,10 +15,8 @@ use GuzzleHttp\Client;
 use GuzzleHttp\Cookie\CookieJar;
 use Luolongfei\Libs\Log;
 use Luolongfei\Libs\Message;
-use Luolongfei\Libs\MessageServices\Mail;
-use Luolongfei\Libs\MessageServices\TelegramBot;
 
-class FreeNom
+class FreeNom extends Base
 {
     const VERSION = 'v0.4';
 
@@ -42,11 +40,6 @@ class FreeNom
     // 匹配登录状态的正则
     const LOGIN_STATUS_REGEX = '/<li.*?Logout.*?<\/li>/i';
 
-    /**
-     * @var FreeNom
-     */
-    protected static $instance;
-
     /**
      * @var Client
      */
@@ -67,7 +60,24 @@ class FreeNom
      */
     protected $password;
 
-    public function __construct()
+    /**
+     * @var FreeNom
+     */
+    private static $instance;
+
+    /**
+     * @return FreeNom
+     */
+    public static function getInstance()
+    {
+        if (!self::$instance instanceof self) {
+            self::$instance = new self();
+        }
+
+        return self::$instance;
+    }
+
+    private function __construct()
     {
         $this->client = new Client([
             'headers' => [
@@ -85,16 +95,8 @@ class FreeNom
         system_log(sprintf('当前程序版本 %s', self::VERSION));
     }
 
-    /**
-     * @return FreeNom
-     */
-    public static function getInstance()
+    private function __clone()
     {
-        if (!self::$instance instanceof self) {
-            self::$instance = new self();
-        }
-
-        return self::$instance;
     }
 
     /**

+ 278 - 0
app/Console/MigrationEnvFile.php

@@ -0,0 +1,278 @@
+<?php
+/**
+ * 迁移 .env 文件
+ *
+ * @author mybsdc <[email protected]>
+ * @date 2021/11/3
+ * @time 15:57
+ */
+
+namespace Luolongfei\App\Console;
+
+use Luolongfei\Libs\Env;
+
+class MigrationEnvFile extends Base
+{
+    /**
+     * @var array 当前已有的环境变量数据
+     */
+    protected $allOldEnvValues;
+
+    /**
+     * @var int 迁移环境变量数量
+     */
+    public $migrateNum = 0;
+
+    /**
+     * @var FreeNom
+     */
+    private static $instance;
+
+    /**
+     * @return FreeNom
+     */
+    public static function getInstance()
+    {
+        if (!self::$instance instanceof self) {
+            self::$instance = new self();
+        }
+
+        return self::$instance;
+    }
+
+    private function __construct()
+    {
+        $this->allOldEnvValues = $this->getAllOldEnvValues();
+    }
+
+    private function __clone()
+    {
+    }
+
+    /**
+     * 获取当前有效的旧的环境变量值
+     *
+     * 会做一些基本的处理,让旧版数据兼容新版 .env 文件
+     *
+     * @return array
+     */
+    protected function getAllOldEnvValues()
+    {
+        $allOldEnvValues = env();
+
+        unset($allOldEnvValues['ENV_FILE_VERSION']);
+
+        $allOldEnvValues = array_filter($allOldEnvValues, function ($val) {
+            return $val !== '';
+        });
+
+        $allOldEnvValues = array_map(function ($val) {
+            $tmpVal = strtolower($val);
+
+            if ($tmpVal === 'true' || $tmpVal === true) {
+                return 1;
+            } else if ($tmpVal === 'false' || $tmpVal === false) {
+                return 0;
+            } else {
+                return $val;
+            }
+        }, $allOldEnvValues);
+
+        return $allOldEnvValues;
+    }
+
+    /**
+     * 是否需要迁移
+     *
+     * @return bool
+     * @throws \Exception
+     */
+    public function isNeedMigration()
+    {
+        $envVer = $this->getEnvFileVer();
+
+        if (is_null($envVer)) {
+            return true;
+        }
+
+        $envExampleVer = $this->getEnvFileVer('.env.example');
+
+        return version_compare($envExampleVer, $envVer, '>');
+    }
+
+    public function getEnvFilePath($filename = '.env')
+    {
+        return ROOT_PATH . DS . $filename;
+    }
+
+    /**
+     * 获取 env 文件版本
+     *
+     * @param string $filename
+     *
+     * @return string|null
+     * @throws \Exception
+     */
+    public function getEnvFileVer($filename = '.env')
+    {
+        $file = $this->getEnvFilePath($filename);
+
+        if (!file_exists($file)) {
+            throw new \Exception('文件不存在:' . $file);
+        }
+
+        if (($fileContent = file_get_contents($file)) === false) {
+            throw new \Exception('读取文件内容失败:' . $file);
+        }
+
+        if (!preg_match('/^ENV_FILE_VERSION=(?P<env_file_version>.*?)$/im', $fileContent, $m)) {
+            return null;
+        }
+
+        return $this->getVerNum($m['env_file_version']);
+    }
+
+    /**
+     * 获取版本号数字部分
+     *
+     * @param $rawVer
+     *
+     * @return string|null
+     */
+    public function getVerNum($rawVer)
+    {
+        if (preg_match('/(?P<ver_num>\d+(?:\.\d+)*)/i', $rawVer, $m)) {
+            return $m['ver_num'];
+        }
+
+        return null;
+    }
+
+    /**
+     * 备份旧文件
+     *
+     * 如果目标文件已存在,将会被覆盖
+     *
+     * @return bool
+     * @throws \Exception
+     */
+    public function backup()
+    {
+        if (copy($this->getEnvFilePath(), $this->getEnvFilePath('.env.old')) === false) {
+            throw new \Exception('备份 .env 文件到 .env.old 文件时出错');
+        }
+
+        return true;
+    }
+
+    /**
+     * 生成新的 .env 文件
+     *
+     * @return bool
+     * @throws \Exception
+     */
+    public function genNewEnvFile()
+    {
+        if (copy($this->getEnvFilePath('.env.example'), $this->getEnvFilePath('.env')) === false) {
+            throw new \Exception('从 .env.example 文件生成 .env 文件时出错');
+        }
+
+        return true;
+    }
+
+    /**
+     * 迁移环境变量数据
+     *
+     * @param array $allEnvVars
+     * @throws \Exception
+     */
+    public function migrateData(array $allEnvVars)
+    {
+        foreach ($allEnvVars as $envKey => $envVal) {
+            if ($this->setEnv($envKey, $envVal)) {
+                $this->migrateNum++;
+            }
+        }
+
+        // 重载环境变量
+        Env::getInstance()->init('.env', true);
+    }
+
+    /**
+     * 写入单个环境变量值
+     *
+     * @param string $key
+     * @param $value
+     *
+     * @return bool
+     */
+    public function setEnv(string $key, $value)
+    {
+        $envFilePath = $this->getEnvFilePath();
+        $contents = file_get_contents($envFilePath);
+
+        $contents = preg_replace("/^{$key}=[^\r\n]*/miu", $this->formatEnvVal($key, $value), $contents, -1, $count);
+
+        return $this->writeFile($envFilePath, $contents) && $count;
+    }
+
+    /**
+     * 格式化环境变量
+     *
+     * @param string $key
+     * @param string|integer $value
+     *
+     * @return string
+     */
+    public function formatEnvVal($key, $value)
+    {
+        return sprintf(is_numeric($value) ? '%s=%s' : "%s='%s'", $key, $value);
+    }
+
+    /**
+     * 覆盖文件内容
+     *
+     * @param string $path
+     * @param string $contents
+     *
+     * @return bool
+     */
+    protected function writeFile(string $path, string $contents): bool
+    {
+        $file = fopen($path, 'w');
+        fwrite($file, $contents);
+
+        return fclose($file);
+    }
+
+    /**
+     * @return bool
+     */
+    public function handle()
+    {
+        try {
+            if (!$this->isNeedMigration()) {
+                return true;
+            }
+
+            system_log('检测到你的 .env 文件内容过旧,程式将根据 .env.example 文件自动更新相关配置项,不要慌张,此操作对已有数据不会有任何影响');
+
+            $this->backup();
+            system_log(sprintf('<green>已完成 .env 文件备份</green>,旧文件位置为 %s/.env.old', ROOT_PATH));
+
+            $this->genNewEnvFile();
+            system_log('已生成新 .env 文件');
+
+            $this->migrateData($this->allOldEnvValues);
+            system_log(sprintf('<green>数据迁移完成</green>,共迁移 %d 条环境变量数据', $this->migrateNum));
+
+            system_log('<green>恭喜,已成功完成 .env 文件升级</green>');
+
+            return true;
+        } catch (\Exception $e) {
+            system_log('升级 .env 文件出错:' . $e->getMessage());
+
+            return false;
+        }
+    }
+}

+ 5 - 1
app/helpers.php

@@ -14,6 +14,7 @@ use Luolongfei\Libs\Log;
 use Luolongfei\Libs\Env;
 use Luolongfei\Libs\Lang;
 use Luolongfei\Libs\PhpColor;
+use Luolongfei\App\Console\MigrationEnvFile;
 
 if (!function_exists('config')) {
     /**
@@ -245,10 +246,13 @@ if (!function_exists('system_check')) {
             if (!file_exists($envFile)) {
                 throw new LlfException(copy(ROOT_PATH . '/.env.example', $envFile) ? 34520007 : 34520008);
             }
+
+            // 检查当前 .env 文件版本是否过低,过低自动升级
+            MigrationEnvFile::getInstance()->handle();
         }
 
         if (!extension_loaded('curl')) {
             throw new LlfException(34520010);
         }
     }
-}
+}

+ 8 - 2
index.php

@@ -1,6 +1,12 @@
 <?php
 /**
- * 腾讯云函数入口文件
+ * 入口文件
+ *
+ * 腾讯云函数版本维护:
+ * 1、去掉顶部的 “#!/usr/bin/env php”,将文件名改为 index.php
+ * 2、将 “define('IS_SCF', false);” 改为 “define('IS_SCF', true);”
+ * 3、使用 function main_handler($event, $context) {} 将下面的 try catch 部分包起来,
+ *    并在 try 最后一行追加 “return '云函数执行成功。';”,在 main_handler 函数的最后一行追加 “return '云函数执行失败。';”
  *
  * @author mybsdc <[email protected]>
  * @date 2019/3/2
@@ -63,7 +69,7 @@ function exception_handler($e)
 function main_handler($event, $context)
 {
     try {
-        system_check(true);
+        system_check();
 
         $class = sprintf('Luolongfei\App\Console\%s', get_argv('c', 'FreeNom'));
         $fn = get_argv('m', 'handle');

+ 5 - 3
libs/Env.php

@@ -16,11 +16,13 @@ class Env extends Base
     /**
      * @var array 环境变量值
      */
-    protected $allValues;
+    protected $allValues = [];
 
-    public function init($fileName = '.env')
+    public function init($fileName = '.env', $overload = false)
     {
-        $this->allValues = file_exists($fileName) ? Dotenv::create(ROOT_PATH, $fileName)->load() : [];
+        if (file_exists(ROOT_PATH . DS . $fileName)) {
+            $this->allValues = $overload ? Dotenv::create(ROOT_PATH, $fileName)->overload() : Dotenv::create(ROOT_PATH, $fileName)->load();
+        }
     }
 
     public function get($key = '', $default = null)

+ 1 - 1
resources/lang/zh.php

@@ -13,7 +13,7 @@ return [
         '34520002' => '登录 freenom 出错。错误信息:%s',
         '34520003' => '域名数据匹配失败,可能是你暂时没有域名或者页面改版导致正则失效,请及时联系作者',
         '34520004' => 'token 匹配失败,可能是页面改版导致正则失效,请及时联系作者',
-        '34520005' => 'putenv() 函数被禁用,无法写入环境变量导致程序无法正常运作,解决方案参考:https://github.com/luolongfei/freenom/issues/22',
+        '34520005' => 'putenv() 函数被禁用,无法写入环境变量导致程序无法正常运作,请启用 putenv() 函数',
         '34520006' => sprintf('不支持 php7 以下的版本,当前版本为%s,请升级到 php7 以上', PHP_VERSION),
         '34520007' => sprintf('已自动在%s目录下生成 .env 配置文件,请将配置文件中的各项内容修改为你自己的', ROOT_PATH),
         '34520008' => sprintf('请将%s目录下的 .env.example 文件复制为 .env 文件,并将 .env 文件中的各项内容修改为你自己的', ROOT_PATH),

+ 6 - 0
run

@@ -3,6 +3,12 @@
 /**
  * 入口文件
  *
+ * 腾讯云函数版本维护:
+ * 1、去掉顶部的 “#!/usr/bin/env php”,将文件名改为 index.php
+ * 2、将 “define('IS_SCF', false);” 改为 “define('IS_SCF', true);”
+ * 3、使用 function main_handler($event, $context) {} 将下面的 try catch 部分包起来,
+ *    并在 try 最后一行追加 “return '云函数执行成功。';”,在 main_handler 函数的最后一行追加 “return '云函数执行失败。';”
+ *
  * @author mybsdc <[email protected]>
  * @date 2019/3/2
  * @time 11:05