composer脚本无法原生支持条件判断,因scripts字段仅将字符串交由系统shell执行,而ci中环境变量未导出、windows语法不兼容、autoload未加载等问题导致if语句失败;应改用php回调函数显式检测环境变量并控制流程。

Composer 脚本无法原生做条件判断(比如“仅当 APP_ENV=prod 时执行”),所有条件逻辑必须由脚本自身实现,而非靠 Composer 解析配置项或环境变量自动跳过。
为什么 scripts 字段里写 if [ "$APP_ENV" = "prod" ]; then php artisan down; fi 会失败
Composer 的 scripts 不是 shell 解释器,它只把字符串当作命令交给系统 shell 执行——但前提是该 shell 环境已就绪、变量已注入、语法被正确解析。而实际运行时:
- 在 CI 中
APP_ENV往往未导出到子 shell,$APP_ENV展开为空 - Windows 上
if语法不兼容,直接报错退出 - Composer 默认不加载项目
autoload.php,所以$_ENV或getenv('APP_ENV')在 PHP 回调中也可能为空 - 即使写了
sh -c "if [ ... ]",错误码 0/1 会被 Composer 当作脚本成功/失败,导致后续命令中断或静默跳过
用 PHP 回调 + 显式环境检测替代 shell 条件语句
可靠的方式是把判断逻辑收口到一个可 autoload 的 PHP 类方法里,由它读取真实运行时环境并决定是否执行后续动作。
- 确保类已注册进
autoload["psr-4"],例如:"App\Scripts\": "src/Scripts/" - 方法必须是
public static,且接受ComposerScriptEvent参数 - 用
$event->getComposer()->getConfig()->get('platform') ?? []或getenv('APP_ENV')获取环境,而不是依赖$_SERVER - 避免直接
exit(0),改用return true或throw new RuntimeException('skipped')控制流程
示例:"post-autoload-dump": "AppScriptsEnvGuard::checkAndDown",其内部可做:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
public static function checkAndDown(Event $event): void
{
if (getenv('APP_ENV') !== 'prod') {
$event->getIO()->writeError('<info>Skipped: not in production</info>');
return;
}
passthru('php vendor/bin/artisan down --no-interaction');
}
如何让同一个脚本在 dev 和 prod 下行为不同
不要靠 composer install --no-dev 自动禁用脚本(它只影响 dev 依赖安装,不跳过 post-* 钩子),而是主动适配:
- 检查
COMPOSER_DEV_MODE环境变量:getenv('COMPOSER_DEV_MODE') === '0'表示当前是--no-dev模式 - 对 dev-only 工具(如
phpunit、infection)加存在性校验:is_executable('vendor/bin/phpunit') - 生产部署常用命令如
cache:clear应加--no-interaction和--env=production,否则可能卡住或读错配置 - 避免在脚本里硬编码
APP_DEBUG=true,应从.env或环境变量实时读取
CI 场景下绕过交互式命令的真正解法
很多人加 --no-interaction 还是卡住,根本原因是某些 Laravel/Symfony 命令在非 TTY 环境下仍会尝试读 stdin(比如数据库迁移确认)。这时不能只靠参数,得组合使用:
- 强制设置
STDIN为非交互:php vendor/bin/artisan migrate --force - 用
timeout 30s包裹防死锁(尤其在 Docker 容器中 DNS 不稳时) - 在 GitHub Actions 中显式声明:
env: { CI: 'true', APP_ENV: 'production' },并在 PHP 脚本中检查getenv('CI') === 'true' - 把“是否跳过”逻辑下沉到命令本身,比如 Laravel 的
migrate支持--pretend,可先验证再执行
最易被忽略的一点:Composer 脚本运行时的当前工作目录(getcwd())未必等于 composer.json 所在目录——尤其在 symlink 部署或 Docker volume 挂载时,./.env 可能根本不存在。所有路径操作请用 __DIR__ 或 $event->getComposer()->getConfig()->get('vendor-dir') 动态获取。










