composer不管理环境变量,仅少数配置支持${var}占位符且依赖php getenv()运行时解析;scripts执行时子进程清空环境变量,仅composer_script_前缀变量可透传;.env需用pre-autoload-dump钩子在自动加载前安全加载,注意文件编码、格式、路径及加载时机。

Composer 本身不管理环境变量,它只在极少数配置字段(如 config.process-timeout、repositories[].url)中支持 ${VAR} 占位符,且仅限运行时由 PHP 的 getenv() 解析——你必须提前把变量塞进 PHP 进程里,否则就是字面量。
为什么 getenv() 在 Composer 脚本里读不到 .env 变量
Composer 执行 scripts 时会 fork 子进程,默认清空几乎所有环境变量,只保留 PATH、HOME 等白名单项。即使你本地 export APP_ENV=prod,脚本里 getenv('APP_ENV') 仍是 false。
真正能透传的只有带 COMPOSER_SCRIPT_ 前缀的变量:
-
COMPOSER_SCRIPT_APP_ENV=prod composer run-script build→ 脚本内getenv('APP_ENV')返回"prod" - 前缀必须全大写,
COMPOSER_SCRIPT_app_env无效 - CI 场景(如 GitHub Actions)需在
env:块中显式定义:COMPOSER_SCRIPT_APP_ENV: ${{ secrets.APP_ENV }} -
${VAR}占位符在composer.json的scripts字段里完全不生效,比如"test": "php -r \"echo ${APP_ENV};\""是错的
如何让 vlucas/phpdotenv 在所有代码执行前就生效
把 $dotenv->load() 放在 index.php 顶部是常见错误:框架或类库可能在 vendor/autoload.php 加载阶段就访问 $_ENV 或 getenv(),此时 .env 还没读。
正确做法是用 pre-autoload-dump 钩子,在自动加载器生成前注入:
- 创建
scripts/load-env.php:<?php $dotenv = Dotenv\Dotenv::createImmutable(__DIR__); $dotenv->safeLoad();
- 在
composer.json中声明:"pre-autoload-dump": "php scripts/load-env.php" - 运行
composer dump-autoload或任意触发 autoload 更新的操作(如composer install) - 使用
safeLoad()而非load(),避免.env缺失时报错,适合多环境部署
加载 .env 文件时最容易踩的坑
vlucas/phpdotenv 对文件格式极其敏感,一个细节不对就整行失效:
-
.env必须保存为 UTF-8 无 BOM 格式;Windows 编辑器常偷偷加 BOM,用file -i .env检查,显示with BOM就要重存 - 等号前后不能有空格:
DB_HOST = localhost会被跳过,必须写成DB_HOST=localhost - 变量名含点号(如
REDIS.HOST)默认被忽略,需手动putenv()注入 - 路径写错是高频问题:若入口在
public/,而.env在项目根目录,__DIR__是public,得写Dotenv::createImmutable(__DIR__.'/..') -
variables_order默认不含E,所以$_ENV为空是正常的;推荐统一用$_SERVER['DB_HOST']或封装env()函数从$_SERVER取值
最隐蔽的问题是加载时机与作用域混用:同一进程里多次调用 Dotenv::load() 可能覆盖已有值,尤其在 CLI 命令(如 php artisan tinker)和 Web 入口共存时,容易出现变量状态不一致。别图省事把它塞进 autoload.files,顺序不可控,风险远大于便利。











