Yii应用启动前必须加载.env文件,否则getenv()无法获取值;需在入口脚本顶部用vlucas/phpdotenv加载,配置中须设默认值,敏感字段DSN需urlencode处理,.env与.env.local应分离管理。

Yii 应用启动前必须加载 .env,否则 getenv() 拿不到值
Yii 本身不自动读取 .env 文件,所有 $_ENV 或 getenv() 调用都依赖 PHP 进程启动时的环境变量状态。如果你在 config/web.php 里直接写 getenv('DB_HOST') 却没提前加载,它永远返回 false 或空字符串。
正确做法是在入口脚本(web/index.php 或 yii 命令行入口)最顶部引入 vlucas/phpdotenv 并加载:
require __DIR__ . '/../vendor/autoload.php'; $dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/..'); $dotenv->load(); // 必须在 Yii::createObject() 之前执行
注意路径要指向含 .env 的目录(通常是项目根),且 createImmutable() 是推荐方式,避免运行时被意外覆盖。
config/main.php 中引用环境变量要加默认值兜底
即使 .env 加载成功,开发机漏配、CI 环境未挂载、或变量名拼错都会导致配置崩掉。Yii 配置是 PHP 数组,不支持“懒加载”,所有 getenv() 在文件解析时就执行了。
安全写法是始终提供 fallback:
getenv('APP_ENV') ?: 'dev'getenv('DB_PORT') ?: '3306'-
(int)getenv('LOG_LEVEL') ?: 200(强转类型防字符串误用)
别信“我本地肯定配好了”——上线后第一个 500 往往就来自某个没设默认值的 getenv('REDIS_URL')。
数据库配置里用 getenv() 要小心 PDO DSN 的特殊字符转义
如果 .env 里写了 DB_PASSWORD="p@ss#word!",直接拼进 DSN:mysql:host=localhost;dbname=testdb;password=p@ss#word! 会因 # 被当注释截断,PDO 连接失败。
正确做法是用 urlencode() 处理敏感字段:
'password' => urlencode(getenv('DB_PASSWORD') ?: ''),
同理处理 DB_USER、DB_NAME(虽然少见,但带 / 或 @ 也会出问题)。别图省事全量 urlencode 整个 DSN —— 只对变量值做,结构部分保持原样。
测试环境和生产环境的 .env 文件不能共用
本地 .env 通常包含调试开关、SQL 日志、Xdebug 配置;生产环境必须禁用这些。硬编码在配置里或靠 APP_ENV 切换不保险 —— 最小风险是把 .env.local(本地专用)和 .env(通用基础)拆开,用 $dotenv->load() 后再手动 $dotenv->safeLoad() 补充本地文件:
$dotenv->load();
if (file_exists(__DIR__.'/../.env.local')) {
$dotenv->safeLoad(__DIR__.'/../.env.local');
}
.env.local 加入 .gitignore,彻底隔离敏感与调试配置。线上部署时只传 .env,不传 .env.local —— 少一个文件,少一个泄露面。
环境变量不是魔法,它只是把字符串塞进进程环境。Yii 不管你从哪来,只认 getenv() 返回那一刻的值。配错路径、漏写默认值、忽略特殊字符,三个点里踩中一个,整个应用就卡在配置层起不来。


















