Env::load() 必须在 App::initialize() 前手动调用,否则配置文件无法读取环境变量;Web 在 public/index.php 中 autoload 后、App::run() 前调用,CLI 需在入口或 handle() 开头显式调用,Swoole 每 worker 启动时均需重载。

Env::load() 必须在 App 初始化前手动调用
ThinkPHP 不会在任何阶段自动执行 Env::load(),它只是一个封装了 vlucas/phpdotenv 的加载入口,是否调用、何时调用,完全由开发者控制。常见错误是以为“装了 think-env 扩展就自动生效”,结果 env('DB_HOST') 一直返回 null 或默认值。
关键时机是:必须在 App::initialize() 被触发之前完成加载,否则后续所有配置文件(如 config/database.php)执行时,env() 函数还没拿到值,就只能 fallback 到默认值。
- Web 环境下,通常加在
public/index.php中require __DIR__.'/../vendor/autoload.php';之后、App::run()之前 - CLI 环境下,不能依赖 HTTP 生命周期,需在
think入口文件末尾或 Command 类的handle()开头显式调用 - 若使用 Swoole 长连接模式,每次 worker 启动都需重新加载,不能只在 master 进程里调一次
Env::load() 和 Dotenv::createImmutable()->safeLoad() 的区别
Env::load() 是 ThinkPHP 封装的快捷方法,底层仍调用 vlucas/phpdotenv;而 Dotenv::createImmutable(__DIR__)->safeLoad() 是原生调用,更可控、报错更明确。两者行为基本一致,但有几点实际差异:
-
Env::load()默认读取config/app.php中的'env_file' => '.env'配置项指定路径,若该配置被注释或写错(比如写成'.env.local'),它就静默失败 -
Dotenv::createImmutable()必须显式传入目录路径(如__DIR__.'/..'),路径错则抛InvalidPathException,更容易定位问题 - 当项目根目录下存在多个 .env 文件(如
.env.prod),Env::load()不支持自动按APP_ENV切换,必须自己拼路径再调用原生方式 - ThinkPHP 8.0+ 推荐直接用原生方式,因为
Env::load()在多环境场景下容易被绕过或覆盖
为什么 Env::load() 执行了却还是读不到变量
最常被忽略的是:加载成功 ≠ 变量可用。即使 Env::load() 没报错,也可能因以下原因导致 env('KEY') 返回空:
立即学习“PHP免费学习笔记(深入)”;
-
.env文件编码不是 UTF-8 无 BOM——ThinkPHP 8 完全静默跳过,不提示、不报错 - 变量名含小写字母或短横线(如
db_host或DB-HOST),env()只认全大写+下划线格式 - 等号两侧有空格(
DB_HOST = 127.0.0.1),整行被 dotenv 解析器丢弃 - 值含
@、!、空格等字符但没加单引号(DB_PWD=pass@123❌ → 应写DB_PWD='pass@123'✅) - 系统级已设置
APP_ENV=prod,框架优先读系统变量,直接跳过.env文件(可验证:getenv('APP_ENV')有值但env('APP_ENV')为空)
CLI 下 Env::load() 容易漏掉的关键点
定时任务、队列消费者、自定义命令这些 CLI 场景,是 Env::load() 最容易失效的地方,因为工作目录和环境上下文与 Web 完全不同:
- 执行命令前必须确保当前 shell 工作目录是项目根目录(
cd /var/www/myapp && php think schedule:run),否则__DIR__指向错误,.env找不到 - systemd 或 supervisor 启动时,默认不继承用户 shell 的环境变量,
APP_ENV、DB_HOST等必须在 service 文件里用Environment=显式声明 - 不能在
bootstrap/app.php里提前调用env('DB_HOST')——此时Env::load()还没执行,会直接触发Dotenv\Exception\InvalidPathException - 运行
php think clear:config后,下次启动仍需重新执行Env::load(),缓存清除不影响加载逻辑本身
真正难的不是写那行 Env::load(),而是确认它在哪一刻被执行、对哪些变量生效、又被哪些外部因素覆盖。调试时别只盯着函数有没有调用,先用 getenv('DB_HOST') 和 print_r($_ENV) 直接看 PHP 环境层到底有没有值。



















