Composer scripts中提示语需绑定read等阻断操作,单用echo无效;Windows用set/p或cross-env;PHP中避免exec("read"),应由shell脚本处理交互并传参,非交互环境自动跳过。

Composer 的 scripts 里怎么写提示语才不会被跳过
默认情况下,composer run-script 执行时不会主动停顿或确认,所谓“提示语”如果只是 echo 一行字,用户很可能直接按回车继续,根本没看到。真正起作用的提示必须绑定到有阻断能力的操作上。
实操建议:
- 用
read -p "⚠️ 该操作将清空测试数据库,确认继续?(y/N) " -n 1 -r这类 shell 命令做交互式确认,而不是只靠echo - 在
composer.json的scripts中,把提示和后续动作写成单条 shell 命令(用&&连接),避免分多行导致逻辑断裂 - Windows 用户注意:
read不可用,得改用set /p或直接依赖 cross-env 工具链统一处理 - 不要把提示逻辑放在 PHP 脚本里再由 Composer 调用——PHP 的
readline()在非交互终端(如 CI)会直接失败,而 Composer 的执行环境常被判定为非交互
为什么 composer run-script setup-dev 总是静默执行
因为 Composer 默认不把 STDIN 流透传给子进程,即使你的脚本里写了 read 或 fgets(STDIN),也会立刻读到 EOF,表现为“没提示、直接往下走”。
解决办法只有两个方向:
- 显式启用交互模式:运行时加
-v(verbose)参数还不够,必须加--no-ansi+php -d variables_order=EGPCS composer.phar run-script setup-dev并确保终端支持 TTY —— 但这不可靠,尤其在 GitLab CI/CD 中基本失效 - 更稳妥的做法是绕过 Composer 的执行封装:把提示和主逻辑打包进一个独立可执行脚本(如
bin/setup-dev),并在composer.json中用"setup-dev": "bash bin/setup-dev"调用,这样能完全控制 stdin/stdout - 检查当前是否在真实终端:
[ -t 0 ] && echo "interactive" || echo "non-interactive",在脚本开头做判断,非交互环境自动跳过提示,避免 CI 卡住
composer.json 里的 scripts 如何区分开发/生产提示语
提示语不是越详细越好,关键要看谁在执行、在哪执行。本地开发需要明确警告副作用(如删库、重置配置),CI 环境则要完全静默或仅输出结构化日志。
推荐做法:
- 用环境变量分流:
"post-install-cmd": "if [ \"$COMPOSER_DEV_MODE\" = \"1\" ]; then bash bin/prompt-check.sh; fi",配合COMPOSER_DEV_MODE=1 composer install - 避免在
pre-autoload-dump这类钩子里放提示——它可能被其他命令间接触发(比如composer update),用户完全没预期 - 提示语中禁止出现模糊动词:“请检查配置”不如“请确认
config/database.php中的DB_HOST已设为 'localhost'”,越具体越不容易被忽略 - 所有提示必须带明确退出路径:用户输入
n或N时,脚本应exit 0(表示用户主动中止,非错误),否则 CI 会当作失败
PHP 脚本调用 exec("read ...") 为什么总报错
PHP 的 exec() 默认不继承父进程的 STDIN,且 shell 内建命令(如 read)在子 shell 中执行后,变量无法回传到 PHP 上下文——你根本拿不到用户按了什么。
这不是权限或路径问题,是模型限制:
- 别试图在 PHP 里用
exec("read -p 'xxx' var"),var只存在于那个瞬间的子 shell,PHP 拿不到 - 真要用 PHP 处理输入,得用
fgets(STDIN),但前提是确保 Composer 是以交互模式启动的(见上一节),且不能用proc_open之类封装过 stdin 的方式 - 最简方案:让 PHP 脚本只负责“干”,把“问”交给外围 shell 脚本完成,用参数传递结果,例如
bash -c 'read -p "env?" e; php do-action.php --env=$e' - 如果团队用的是 Laravel,优先考虑
php artisan命令替代 Composer script,它的$this->confirm()方法已封装好 TTY 检测和 fallback 逻辑
真正的难点不在怎么写出提示,而在判断什么时候不该提示——比如自动化部署流程中,任何阻塞式交互都是定时炸弹。规范的本质,是让提示只出现在人真的需要决策的地方,而不是为了“看起来有流程”。


















