Composer run-script不继承shell环境变量,须用COMPOSER_SCRIPT_*前缀透传或在PHP脚本中通过getenv()读取;动态逻辑应由脚本解析$argv或加载.env实现。

run-script 怎么读取环境变量并动态执行不同逻辑
Composer 的 run-script 本身不解析 shell 环境变量,它只把参数原样传给脚本(PHP 或 shell 命令)。所以直接写 "scripts": {"post-install-cmd": "echo $APP_ENV"} 在 Windows/macOS/Linux 上行为不一致,且大多数情况下输出空字符串。
真正可靠的方式是:让 Composer 把环境变量显式透传进 PHP 脚本,或用 shell 封装层做判断。推荐用 PHP 脚本作为中间层,因为可控性强、跨平台稳定。
- 在
composer.json中定义脚本时,用php scripts/deploy-hook.php这类明确路径,而不是裸命令 - 确保
deploy-hook.php文件有可执行权限(Linux/macOS)或通过php显式调用(Windows 安全) - 不要依赖
$_ENV—— 它可能为空;改用getenv('APP_ENV')或$_SERVER['APP_ENV'],并在 CLI 模式下验证过可用
如何在 post-autoload-dump 钩子里区分 dev/prod 并生成不同配置
这个钩子常被用来生成缓存、构建路由或写入环境相关配置。但 post-autoload-dump 触发时机早于 APP_ENV 是否已由 .env 加载,所以不能依赖框架的环境解析器(如 Laravel 的 App::environment()),必须用原始系统级方式判断。
一个轻量做法是:在 composer.json 的 script 中显式传参,比如:
"scripts": {
"post-autoload-dump": [
"@php scripts/env-aware-build.php --env=$APP_ENV"
]
}
然后在 env-aware-build.php 里用 $argv 解析 --env, fallback 到 getenv('APP_ENV') ?: 'prod'。这样既兼容手动运行 composer run-script post-autoload-dump,也兼容 CI 中 APP_ENV=staging composer install 的场景。
- 避免在脚本里硬编码
if (file_exists('.env')) { ... }—— .env 可能被 gitignore,且不是所有项目都用它 - CI 环境通常不写 .env,而是靠 CI 变量注入,所以
getenv()是唯一通用入口 - 如果脚本需要写文件(如生成
config/compiled.php),注意权限:CLI 用户和 Web 服务器用户可能不同,别让 Web 请求时因文件不可读报错
为什么 vendor/bin/heroku-php-apache2 这类命令无法被 run-script 直接接管
Heroku、Laravel Envoyer 等平台的部署钩子常依赖特定二进制命令,而 run-script 默认工作目录是项目根,不是 vendor/bin。直接写 "post-deploy": "heroku-php-apache2" 会报 Command not found。
根本原因是 PATH 没包含 vendor/bin,且 Composer 不自动 prepends 它。解决方案只有两个:
- 用绝对路径:
"post-deploy": "php vendor/bin/heroku-php-apache2"(前提是该命令是 PHP 脚本) - 用 shell 包裹:
"post-deploy": "PATH="vendor/bin:$PATH" heroku-php-apache2"(仅限 Unix-like 系统) - Windows 下必须用
call vendor\bin\xxx.bat或统一走php vendor/bin/xxx,否则 cmd.exe 找不到可执行文件
特别注意:某些二进制(如 phpstan)是 Phar 封装,必须用 php phpstan.phar 方式调用,不能当系统命令直跑。
APP_ENV=dev composer install 后,post-root-package-install 钩子仍按 prod 执行?
这是最隐蔽的坑:Composer 的 script 钩子(如 post-root-package-install)是在 Composer 自身进程里执行的,它**不会继承父 shell 的环境变量**,除非你显式用 exec 或 proc_open 重传。也就是说,APP_ENV=dev composer install 中的 APP_ENV 只对 composer install 主进程有效,对后续触发的 script 钩子无效。
验证方法:在钩子脚本开头加 file_put_contents('env-debug.log', print_r($_ENV, true));,你会发现里面没有 APP_ENV。
- 临时解法:在
composer.json中用export APP_ENV=$APP_ENV && php scripts/hook.php(Unix)或set APP_ENV=%APP_ENV% && php scripts/hook.php(Windows) - 长期建议:把环境判断逻辑上提到 Composer 插件层(写
PluginInterface),它能拿到完整 CLI 参数和 env - 更务实的做法:放弃在钩子里做复杂分支,只做“无害操作”(如清理缓存),把环境敏感逻辑交给应用启动时的 bootstrap 流程
环境变量透传这件事,在 Composer script 层就是个半残能力 —— 它设计初衷只是简化命令别名,不是做环境编排。真要健壮控制,得往外走一层:用 Makefile、Justfile 或 GitHub Actions 的 steps 来调度。


















