thinkphp 8.0 cli模式下env()返回null,主因是默认不加载.env文件,须在think入口末尾手动调用dotenv::createimmutable(__dir__)->safeload(),并确保.env位于项目根目录、utf-8无bom、避免缓存干扰。

CLI 模式下 env() 返回 null 或旧值
ThinkPHP 在 CLI 模式(如 php think migrate)中不会自动加载 .env,因为 HTTP 生命周期里的自动加载逻辑不触发。此时 env('DB_HOST') 可能返回 null、false 或上一次缓存的旧值,尤其当你在 Web 请求中已加载过 .env 且没清缓存时。
根本原因是:CLI 进程是全新启动的,它不继承 Web 服务器的环境变量,也不复用已加载的 $_ENV;而 ThinkPHP 的配置缓存(runtime/config.php)一旦生成,就会把当时解析出的 env() 结果固化进去——如果这个缓存是 Web 下生成的,CLI 就会误用它。
- 确认是否手动调用了
Dotenv\Dotenv::createImmutable(__DIR__.'/..')->load()—— 必须在think入口脚本(think文件本身或thinkphp/base.php)中,在App::initialize()前执行 - 不要依赖
public/index.php中的加载逻辑——CLI 不走这个入口 - 检查是否在
think脚本顶部设置了think\Env::set('APP_ENV', 'prod'),否则即使.env加载了,APP_ENV仍可能是默认的''或'testing' - 运行
php think clear:config后再试,避免缓存干扰判断
getenv('APP_ENV') 和 env('APP_ENV') 输出不一致
这是最典型的信号:说明系统级环境变量和 .env 文件内容冲突了。比如你在 Linux 上执行过 export APP_ENV=dev,又在 .env 里写了 APP_ENV=prod,那么 getenv('APP_ENV') 返回 dev,而 env('APP_ENV')(经 vlucas/phpdotenv 解析)返回 prod —— 但 ThinkPHP 6+ 默认优先使用系统变量覆盖 .env。
这种不一致会导致多环境配置目录(如 config/prod/)根本不会被加载,因为框架实际读取的是系统变量值。
- 在命令行中先执行
unset APP_ENV,再运行php think run,排除 shell 级污染 - 若使用 PHP-FPM 或 systemd 部署,检查
www.conf或 service 文件中是否有env[APP_ENV]=...设置,它会强覆盖.env - 在控制器里加一行
dump([getenv('APP_ENV'), env('APP_ENV')]);,快速定位哪边被优先采用 - ThinkPHP 8.0+ 支持通过
Dotenv\Dotenv::createMutable()并调用overload()强制用.env覆盖系统变量,但需谨慎——可能破坏容器部署约定
多环境文件(.env.prod)在 CLI 下完全不加载
ThinkPHP 8.0 支持按 APP_ENV 自动加载 .env.{APP_ENV},但这功能只在显式调用 load() 时生效,且要求主 .env 文件存在。如果 CLI 下只存在 .env.prod 而没有 .env,vlucas/phpdotenv 默认不会扫描后缀文件。
更隐蔽的问题是:ThinkPHP 的 think 命令入口未像 public/index.php 那样默认集成该逻辑,必须手动补全。
- 确保项目根目录下同时存在
.env(通用配置)和.env.prod(环境专属),哪怕.env是空文件 - 在
think文件顶部添加:$dotenv = Dotenv\Dotenv::createImmutable(__DIR__, ['env', 'env.' . (getenv('APP_ENV') ?: 'dev')]);<br>$dotenv->load(); - 注意
createImmutable()第二个参数是文件名数组,不是 glob 模式,不能写成['.env*'] - Linux/macOS 下确认
.env.prod编码为 UTF-8 无 BOM,否则静默失败
为什么 config('database.hostname') 对了,但 env('DB_HOST') 还是空
这说明配置已从缓存中合并成功,但 env() 函数本身没读到原始值——典型症状是 .env 文件编码错误(含 BOM)或路径不对。ThinkPHP 的 config() 是最终合并结果,它可能来自缓存文件、硬编码、运行时 set,甚至 fallback 默认值;而 env() 只反映当前 vlucas/phpdotenv 解析状态,二者来源不同。
最容易被忽略的是:CLI 下 __DIR__ 指向的是 think 脚本所在目录(通常是项目根目录),但如果项目结构异常(例如 think 被软链到别处),dirname(__DIR__) .'/.env' 就会拼错路径。
- 在
think文件中加一行var_dump(file_exists(dirname(__DIR__) .'/.env'));,确认路径是否真能访问到文件 - 用
file -i .env检查编码,输出含with BOM就立刻重存为无 BOM UTF-8 - 不要在
config/database.php里写'hostname' => env('DB_HOST') ?: '127.0.0.1'——?:会把false当空,应改用env('DB_HOST', '127.0.0.1') - 如果用 Swoole 长连接,每次 reload 后必须重新加载
.env,不能靠首次初始化
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











