Composer仅支持${env:VAR}格式静态替换,限于repositories、config、extra字段,不支持拼接、嵌套或require/ autoload中使用,2.2+起支持默认值${env:VAR:default}。

composer.json 里不能直接写环境变量,但可以用 ${env:VAR_NAME} 占位
Composer 原生不支持在 composer.json 中执行 shell 命令或读取运行时环境变量,但它提供了有限的变量替换机制:只认 ${env:XXX} 这种格式,且仅在几个特定字段生效(如 repositories、config、extra)。它不是 PHP 的 getenv(),而是在 Composer 加载 composer.json 时由其内部解析器静态展开的。
这意味着你不能在 require 或 autoload 里用 ${env:FOO},也不能嵌套(比如 ${env:BASE_URL}/api 不行)。
-
${env:DB_HOST}✅ 可用(前提是系统环境里真有这个变量) -
${env:DB_HOST:127.0.0.1}✅ 支持默认值(Composer 2.2+) -
https://${env:DOMAIN}/pkg❌ 不支持拼接,会原样保留字符串 -
"php": "${env:PHP_VERSION}"❌config.platform.php字段允许,但require里的版本约束不行
最常用场景:私有仓库地址动态化(repositories)
当你有多个环境(dev/staging/prod)对应不同私有 Packagist 地址时,用环境变量切换最安全。例如:
{
"repositories": [
{
"type": "composer",
"url": "${env:PRIVATE_REPO_URL}"
}
],
"require": {
"myorg/internal-lib": "^1.0"
}
}
然后执行前设置环境变量:
PRIVATE_REPO_URL=https://packagist.dev.myorg.com composer install
注意:composer install 会把最终解析后的 URL 写进 composer.lock,所以不同环境必须各自跑一次 install 或 update,否则 lock 文件会混用。
config 和 extra 字段是环境变量的主要落脚点
config 用于影响 Composer 自身行为(如镜像、超时),extra 则常被插件读取(如 hirak/prestissimo 或自定义部署脚本)。两者都支持 ${env:...}:
{
"config": {
"github-protocols": ["https"],
"process-timeout": 3600,
"fxp-asset": {
"enabled": false
}
},
"extra": {
"deploy-host": "${env:DEPLOY_HOST}",
"app-env": "${env:APP_ENV:production}"
}
}
上面的 ${env:APP_ENV:production} 表示若 APP_ENV 未设置,则默认为 production。这个语法只在 Composer 2.2+ 支持;老版本会报错或忽略整个字段。
PHP 代码中读取这些值,得靠插件或自己解析 composer.json —— Composer 不自动注入到 $_ENV 或 getenv()。
别指望 composer.json 替代配置文件,复杂逻辑交给 PHP 或 dotenv
如果你需要根据环境加载不同依赖、启用不同 autoloader、或做条件 require,composer.json 的变量机制完全不够用。这时候应该:
- 用
.env+vlucas/phpdotenv在应用启动时加载配置 - 把“环境相关参数”移到
config/目录下的 PHP 文件中,用if (getenv('APP_ENV') === 'dev')控制逻辑 - 避免在
composer.json里塞业务逻辑,它本质是包管理契约,不是配置中心
真正容易被忽略的一点:环境变量在 CI/CD 流水线中常被截断或未导出(比如 GitHub Actions 默认不继承 secrets 到子 shell),导致 ${env:SECRET_TOKEN} 展开为空 —— 此时 Composer 不报错,只是静默使用空值,可能拉错仓库或跳过认证。










