Composer脚本中getenv()返回空,因其默认以“干净环境”运行,仅保留PATH、HOME等白名单变量;需用COMPOSER_SCRIPT_前缀显式注入(如COMPOSER_SCRIPT_APP_ENV),并在同一shell命令行中设置,PHP脚本中须调用getenv('COMPOSER_SCRIPT_APP_ENV')读取。

Composer脚本里为什么getenv()返回空?
因为 Composer 默认以“干净环境”运行脚本,只保留 PATH、HOME 等极少数白名单变量,其他系统级或 .env 文件加载的变量一律被清空。这不是 PHP 配置问题,是 Composer 主动剥离环境的安全策略。
常见错误现象:
-
$_ENV['APP_ENV']或$_SERVER['APP_ENV']始终为null - 用
Dotenv::load()或vlucas/phpdotenv在脚本里加载.env失败——脚本工作目录不一定是项目根,.env路径容易错 -
putenv('APP_ENV=prod')后再调getenv('APP_ENV')仍为空——脚本启动时环境已固化,putenv()不影响已派生进程
怎么让变量真正进 Composer 脚本?
唯一被 Composer 原生支持的方式:用 COMPOSER_SCRIPT_ 前缀显式注入,并在 shell 层面传入。
实操要点:
- 变量名必须全大写,且带
COMPOSER_SCRIPT_前缀,例如想传APP_ENV,就得设COMPOSER_SCRIPT_APP_ENV - 必须在执行
composer run-script的同一行 shell 命令中设置,例如:APP_ENV=prod COMPOSER_SCRIPT_APP_ENV=prod composer run-script build - 左边的
APP_ENV=prod对当前 shell 有效,但不会透传给 Composer 子进程;只有右边那个COMPOSER_SCRIPT_APP_ENV=prod才会被 Composer 捕获并注入到脚本环境 - PHP 脚本中必须用
getenv('COMPOSER_SCRIPT_APP_ENV')读取,不要用$_ENV—— 因为variables_order中若没开E,$_ENV就不会自动填充
脚本中读取环境变量的稳妥写法
别依赖任何自动填充机制,直接调 getenv() 并做空值 fallback。
示例(放在你的脚本 PHP 文件里):
$appEnv = getenv('COMPOSER_SCRIPT_APP_ENV') ?: 'dev';
$debug = (bool) getenv('COMPOSER_SCRIPT_DEBUG');
if ($appEnv === 'prod' && !$debug) {
// 生产逻辑
}
注意:
- 不要写
$_ENV['COMPOSER_SCRIPT_APP_ENV'],它不可靠 - 避免在脚本里调
exec('php -r "echo getenv(\'COMPOSER_SCRIPT_APP_ENV\');") - 如果要用多个变量,每个都得加
COMPOSER_SCRIPT_前缀,不能批量传一个数组或 JSON 字符串
为什么 config --global scripts.xxx 没用?
因为 composer config --global scripts.xxx 只是往全局 config.json 写配置,而全局配置里的 scripts 不会被 Composer 加载执行——它只认项目根目录下的 composer.json 中的 scripts 字段。
换句话说:
- 全局
config可设github-oauth、repositories、bin-dir等,但不支持全局脚本定义 - 所有自定义脚本必须定义在项目级
composer.json的scripts下 - 想复用脚本逻辑?用外部 PHP 文件 +
"scripts": {"build": "php build.php"},而不是试图塞进 global config
最易被忽略的一点:脚本执行时的当前工作目录(getcwd())是运行 composer run-script 的路径,不是 composer.json 所在目录,也不是你想象中的项目根——这点直接影响文件路径和 .env 加载位置。











