手撕 Env类. 从启动参数,或从.env文件指定配置文件

lsmir2

使用之式

1.环境变量 -e APP_ENV=development 文件名字 .env.development

php start.php start -e  APP_ENV=development

2.不使用环境变量, 先去找.env.development 找不到再去找.env.production 都没有就摆烂.

找一个僻静角落,轻轻放入.

<?php

namespace app\common;
class Env
{
    public static function init(): void
    {
        global $argv;
        $env = false;
        $base_path = base_path() . '/.env.';
        // 从环境变量获取 APP_ENV
        foreach ($argv as $key => $value) {
            if ($value == '-e' && isset($argv[$key + 1]) && str_contains($argv[$key + 1], '=')) {
                list($name, $value) = explode('=', $argv[$key + 1]);
                $envFilePath = $base_path . $value;
                if ($name == 'APP_ENV' && file_exists($envFilePath)) {
                    $env = self::parseEnvFile($envFilePath);
                    if ($env) {
                        self::initEnv($env, $value);
                        return;
                    }
                }
            }
        }
        // 如果没有环境变量,则从 .env.development 或 .env.production 中获取
        $file = 'development';
        if (!file_exists($base_path . $file)) $file = 'production';
        $env = self::parseEnvFile($base_path . $file);
        if (!$env) {
            error_log("Error: .env.development or .env.production file not found.");
            exit('Error LINE:' . __LINE__);
        }
        self::initEnv($env, $file);
    }

    private static function parseEnvFile($file): false|array
    {
        if (!file_exists($file)) return false;
        return parse_ini_file($file, true) ?? false;
    }

    /**
     * @param array $env
     * @param string $envFilePath
     * @return void
     */
    protected static function initEnv(array $env, string $envFilePath): void
    {
        self::printEnv($envFilePath);
        //初始化ENV 配置
        foreach ($env as $key => $val) {
            //过滤首字母#或者'//'开头的注释
            if (str_starts_with($key, '#') || str_starts_with($key, '//')) continue;
            if (is_array($val)) {
                foreach ($val as $k => $v) { //如果是二维数组 item = PHP_KEY_KEY
                    $item = $key . '_' . $k;
                    putenv("$item=$v");
                }
            } else {
                putenv("$key=$val");
            }
        }

    }

    /**
     * 多进程下只输出一次 env 名字
     * @param string $envFilePath .env 名字
     * @return void
     */
    private static function printEnv(string $envFilePath): void
    {
        $lockFilePath = runtime_path() . '/env_printed.lock';
        $timeout = 3;

        $lockFile = fopen($lockFilePath, 'a+');
        if ($lockFile === false) {
            error_log("Could not open lock file: " . $lockFilePath);
            return;
        }
        if (!flock($lockFile, LOCK_EX)) {
            error_log("Could not acquire lock on file: " . $lockFilePath);
            fclose($lockFile);
            return;
        }
        try {
            if (file_exists($lockFilePath)) {
                $modifiedTime = filemtime($lockFilePath);
                if (time() - $modifiedTime > $timeout) {
                    // 锁失效,输出语句并更新时间
                    echo "Load env file: " . $envFilePath . PHP_EOL;
                    touch($lockFilePath); // 更新修改时间为当前时间
                }
                // 如果锁未失效,不输出
            } else {
                // 文件不存在,输出语句并创建文件
                echo "Load env file: " . $envFilePath . PHP_EOL;
                touch($lockFilePath); // 创建文件并设置修改时间为当前时间
            }
        } finally {
            flock($lockFile, LOCK_UN);
            fclose($lockFile);
        }
    }
}

掰开 config/server.php
在return 之前加入一行

\app\common\Env::init();
return [....];

使用 getenv获取环境变量
或者封装一个助手函数

function envs($key, $default = null): string|array|null
{
    $value = getenv($key, true);
    if ($value === false) return $default;
    return $value;
}
2188 4 3
4个评论

euii

第二部分,//从.env 文件获取 WENMAN_ENV=development env文件全名 .env.development
.env.development没有判断是否存在文件,如果在.env里配置了WENMAN_ENV但是却没有这个文件,会报错。修正如下:

if (isset($env['WENMAN_ENV']) && file_exists(base_path().'/.env'.$env['WENMAN_ENV'])) {
                    $env = parse_ini_file(base_path() . '/.env.' . $env['WENMAN_ENV'], true);
                }
  • lsmir2 2024-11-21

    对,我已经改了好多版了这里忘记改了

深路潇湘

很棒,建议官方添加支持变成feature

  • 暂无评论
tanhongbin

啥需求,没看懂

  • 暂无评论
afeia

请问有没有遇到加载环境配置文件是默认的环境配置的问题,
打印看了下是因为每个进程加载一次配置。因为最后一个进程没有环境变量所以加载默认配置。
目前不知道该如何解决,请问大佬有什么办法吗。

补充:
之前我是在windows环境测试的。刚试下了两个环境,Linux下没问题,windows有问题。

windows;

  • lsmir2 2026-01-19

    没有遇到你这个问题,你把webman 启动后的控制台截图我看下
    截图

  • lsmir2 2026-01-19

    你可以在env类 加入

     echo getmypid().'\n'; 看看是那些进程有加载 那些进程没加载
  • lsmir2 2026-01-19

    代码更新了 是我现在用的你试试

  • afeia 2026-01-20

    大佬有空你试下

  • lsmir2 2026-01-20

    最后一个进程是 monitor 没有调用 config/server.php

    \app\common\Env::init();

    初始化直接改到windows.php 进行初始化 其实不改也正常使用吧? 我开发用黑苹果线上是linux环境呢.

  • afeia 2026-01-21

    windows不改是有问题的,有空我再研究windows环境怎么处理。谢谢大佬!

lsmir2

2480
积分
0
获赞数
0
粉丝数
2024-01-29 加入
🔝