From ca1d2a1ed8fa51084f5cb1a11cae87267bc6a946 Mon Sep 17 00:00:00 2001 From: sunxyw <31698606+sunxyw@users.noreply.github.com> Date: Sun, 15 Jan 2023 21:30:44 +0800 Subject: [PATCH] add environment variables support (#255) --- composer.json | 3 +- config/container.php | 3 + config/global.php | 8 +-- src/Globals/global_functions.php | 10 ++++ src/ZM/Bootstrap/LoadConfiguration.php | 23 ++++++++ src/ZM/Config/Environment.php | 82 ++++++++++++++++++++++++++ src/ZM/Config/EnvironmentInterface.php | 23 ++++++++ 7 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 src/ZM/Config/Environment.php create mode 100644 src/ZM/Config/EnvironmentInterface.php diff --git a/composer.json b/composer.json index 33816409..bb42d5a9 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,8 @@ "symfony/console": "^6.0", "symfony/polyfill-ctype": "^1.19", "symfony/polyfill-mbstring": "^1.19", - "symfony/routing": "~6.0 || ~5.0 || ~4.0" + "symfony/routing": "~6.0 || ~5.0 || ~4.0", + "vlucas/phpdotenv": "^5.5" }, "require-dev": { "captainhook/captainhook": "^5.10", diff --git a/config/container.php b/config/container.php index 5a79ffda..9d4169db 100644 --- a/config/container.php +++ b/config/container.php @@ -5,6 +5,8 @@ declare(strict_types=1); use OneBot\Driver\Driver; use OneBot\Driver\Process\ProcessManager; use Psr\Log\LoggerInterface; +use ZM\Config\Environment; +use ZM\Config\EnvironmentInterface; use ZM\Framework; /* @@ -24,5 +26,6 @@ return [ 'worker_id' => fn () => ProcessManager::getProcessId(), Driver::class => fn () => Framework::getInstance()->getDriver(), LoggerInterface::class => fn () => logger(), + EnvironmentInterface::class => Environment::class, ], ]; diff --git a/config/global.php b/config/global.php index ce6a04b0..f1c2428b 100644 --- a/config/global.php +++ b/config/global.php @@ -3,7 +3,7 @@ declare(strict_types=1); /* 启动框架的底层驱动(原生支持 swoole、workerman 两种) */ -$config['driver'] = 'workerman'; +$config['driver'] = env('DRIVER', 'workerman'); /* 要启动的服务器监听端口及协议 */ $config['servers'] = [ @@ -22,14 +22,14 @@ $config['servers'] = [ /* Workerman 驱动相关配置 */ $config['workerman_options'] = [ - 'worker_num' => 1, // 如果你只有一个 OneBot 实例连接到框架并且代码没有复杂的CPU密集计算,则可把这里改为1使用全局变量 + 'worker_num' => env('WORKER_NUM', 1), // 如果你只有一个 OneBot 实例连接到框架并且代码没有复杂的CPU密集计算,则可把这里改为1使用全局变量 ]; /* Swoole 驱动相关配置 */ $config['swoole_options'] = [ 'coroutine_hook_flags' => SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL), // 协程 Hook 内容 'swoole_set' => [ - 'worker_num' => 1, // 如果你只有一个 OneBot 实例连接到框架并且代码没有复杂的CPU密集计算,则可把这里改为1使用全局变量 + 'worker_num' => env('WORKER_NUM', 1), // 如果你只有一个 OneBot 实例连接到框架并且代码没有复杂的CPU密集计算,则可把这里改为1使用全局变量 'dispatch_mode' => 2, // 包分配原则,见 https://wiki.swoole.com/#/server/setting?id=dispatch_mode 'max_coroutine' => 300000, // 允许最大的协程数 'max_wait_time' => 5, // 安全退出模式下允许等待 Worker 的最长秒数 @@ -51,7 +51,7 @@ $config['runtime'] = [ ], 'namespace' => [], ], - 'timezone' => 'Asia/Shanghai', + 'timezone' => env('TIMEZONE', 'Asia/Shanghai'), ]; /* 允许加载插件形式 */ diff --git a/src/Globals/global_functions.php b/src/Globals/global_functions.php index 432b3c3b..8b3444d4 100644 --- a/src/Globals/global_functions.php +++ b/src/Globals/global_functions.php @@ -7,6 +7,7 @@ use OneBot\Driver\Coroutine\CoroutineInterface; use OneBot\Driver\Process\ExecutionResult; use OneBot\V12\Object\MessageSegment; use Psr\Log\LoggerInterface; +use ZM\Config\Environment; use ZM\Config\ZMConfig; use ZM\Container\ContainerHolder; use ZM\Logger\ConsoleLogger; @@ -268,3 +269,12 @@ function kv(string $name = ''): Psr\SimpleCache\CacheInterface /* @phpstan-ignore-next-line */ return is_a($kv_class, KVInterface::class, true) ? $kv_class::open($name) : new $kv_class($name); } + +/** + * 获取环境变量 + */ +function env(string $key, mixed $default = null): mixed +{ + // TODO: 重新思考容器绑定的加载方式,从而在此处使用 interface + return resolve(Environment::class)->get($key, $default); +} diff --git a/src/ZM/Bootstrap/LoadConfiguration.php b/src/ZM/Bootstrap/LoadConfiguration.php index a67cf330..44b9ea57 100644 --- a/src/ZM/Bootstrap/LoadConfiguration.php +++ b/src/ZM/Bootstrap/LoadConfiguration.php @@ -4,13 +4,20 @@ declare(strict_types=1); namespace ZM\Bootstrap; +use Dotenv\Dotenv; use OneBot\Driver\Workerman\Worker; +use ZM\Config\Environment; +use ZM\Config\EnvironmentInterface; use ZM\Config\ZMConfig; class LoadConfiguration { public function bootstrap(array $config): void { + // TODO: 重新思考容器绑定的加载方式,从而在此处使用 interface + $env = resolve(Environment::class); + $this->loadEnvVariables($env); + $config_i = config(); $config_i->addConfigPath($this->getConfigDir($config)); $config_i->setEnvironment($this->getConfigEnvironment($config)); @@ -68,4 +75,20 @@ class LoadConfiguration } } } + + private function loadEnvVariables(EnvironmentInterface $env): void + { + $dotenv_path = $env->get('DOTENV_PATH', SOURCE_ROOT_DIR . '/.env'); + + if (!file_exists($dotenv_path)) { + return; + } + + $path = dirname($dotenv_path); + $file = basename($dotenv_path); + + foreach (Dotenv::createImmutable($path, $file)->load() as $key => $value) { + $env->set($key, $value); + } + } } diff --git a/src/ZM/Config/Environment.php b/src/ZM/Config/Environment.php new file mode 100644 index 00000000..d78de144 --- /dev/null +++ b/src/ZM/Config/Environment.php @@ -0,0 +1,82 @@ + true, + '(true)' => true, + 'false' => false, + '(false)' => false, + 'null' => null, + '(null)' => null, + 'empty' => '', + ]; + + private array $values; + + /** + * @param array $values 额外的环境变量,优先级高于系统环境变量 + * @param bool $overwrite 是否允许后续 set() 覆盖已有的环境变量 + */ + public function __construct( + array $values = [], + private bool $overwrite = false + ) { + $this->values = $values + $_ENV + $_SERVER; + } + + /** + * {@inheritdoc} + */ + public function set(string $name, mixed $value): self + { + if (array_key_exists($name, $this->values) && !$this->overwrite) { + // 如不允许覆盖已有的环境变量,则不做任何操作 + return $this; + } + + $this->values[$name] = $_ENV[$name] = $value; + putenv("{$name}={$value}"); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function get(string $name, mixed $default = null): mixed + { + if (isset($this->values[$name])) { + return $this->normalize($this->values[$name]); + } + + return $default; + } + + /** + * {@inheritdoc} + */ + public function getAll(): array + { + $result = []; + + foreach ($this->values as $key => $value) { + $result[$key] = $this->normalize($value); + } + + return $result; + } + + protected function normalize(mixed $value): mixed + { + if (!is_string($value)) { + return $value; + } + + return self::VALUE_MAP[strtolower($value)] ?? $value; + } +} diff --git a/src/ZM/Config/EnvironmentInterface.php b/src/ZM/Config/EnvironmentInterface.php new file mode 100644 index 00000000..368601ba --- /dev/null +++ b/src/ZM/Config/EnvironmentInterface.php @@ -0,0 +1,23 @@ +