Composer脚本中getenv('APP_ENV')读不到值是因为子进程不继承环境变量,需用COMPOSER_SCRIPT_APP_ENV前缀透传或手动加载phpdotenv。

Composer 脚本里直接用 getenv('APP_ENV') 或 $_ENV['APP_ENV'] 读不到值,不是你漏写了 .env,也不是 PHP 配置错了——是 Composer 启动的子进程默认不继承 shell 环境变量,尤其在 CI/CD、Docker 或 Windows 下更明显。
为什么 getenv() 在 Composer 脚本里返回 false
Composer 执行脚本时会 fork 一个“干净环境”的子进程,只保留 PATH、HOME 等极少数白名单变量,其余全被清空。这意味着:
-
export APP_ENV=prod && composer run-script build——APP_ENV不会透传进脚本 -
$_ENV默认不可用,除非显式配置variables_order="EGPCS",但 Composer 不帮你设这个 -
$_SERVER相对可靠,因为 Composer 会把部分环境变量自动塞进去(只要没被 unset) - Linux/macOS 下用
env VAR=xxx php script.php可行,但composer run-script不支持这种写法
正确注入变量:必须用 COMPOSER_SCRIPT_* 前缀
Composer 唯一原生支持的透传方式,是把变量名加上 COMPOSER_SCRIPT_ 前缀。它会在执行脚本前自动剥离前缀,并注入到子进程环境里:
- 命令行中这样写:
COMPOSER_SCRIPT_APP_ENV=prod COMPOSER_SCRIPT_CI=true composer run-script build - 脚本里就读:
getenv('APP_ENV')→"prod",getenv('CI')→"true" - 大小写敏感:
COMPOSER_SCRIPT_app_env不会被识别,必须全大写 - CI 场景下(如 GitHub Actions),应在
env:块里定义COMPOSER_SCRIPT_APP_ENV: ${{ secrets.APP_ENV }},而不是直接写APP_ENV
${VAR_NAME} 占位符只在 composer.json 的 config 字段生效
这个语法不是给 PHP 脚本用的,而是让 Composer 自己在解析配置时做替换:
- 支持的字段包括:
process-timeout、http-basic凭据、repositories地址等 - 示例:
"config": { "process-timeout": "${PROCESS_TIMEOUT:-300}" },运行时会尝试读取PROCESS_TIMEOUT环境变量,未设置则 fallback 为 300 - 但它不会影响你写的 PHP 脚本逻辑,
composer.json里写"scripts": { "test": "php -r \"echo ${APP_ENV};\""}是无效的 —— 这个${}不会被 shell 展开 - 若真想在脚本里用,得靠
COMPOSER_SCRIPT_*或手动加载vlucas/phpdotenv
本地开发想用 .env?得自己加载
Composer 不会自动读 .env,哪怕你装了 vlucas/phpdotenv。必须在脚本开头主动初始化:
- 先装依赖:
composer require vlucas/phpdotenv - 脚本第一行加:
Dotenv\Dotenv::createImmutable(__DIR__)->load(); - 它默认只往
$_SERVER注入,所以读变量要用$_SERVER['DB_HOST'] ?? 'localhost',别用$_ENV - 多环境可指定文件:
Dotenv::createImmutable(__DIR__, ".env.{$env}")->load();,其中$env来自COMPOSER_SCRIPT_APP_ENV或其他可靠来源 - 注意:这个库不影响 Composer 自身行为,只服务你的 PHP 脚本
最易被忽略的一点:你在 shell 里 echo $APP_ENV 能看到值,不代表 composer run-script 里的 PHP 进程也能看到——环境变量不会自动跨进程传递,必须显式声明或注入。信 getenv('COMPOSER_SCRIPT_APP_ENV'),别信 getenv('APP_ENV')。


















