Composer脚本读取用户输入需用PHP原生方式,优先检测stream_isatty(STDIN)判断交互环境,再用fgets(STDIN)或readline()读取并trim()处理,输出后调用flush()确保提示可见。

Composer脚本里怎么读取用户输入?
Composer 本身不提供交互式输入 API,composer run-script 执行的是普通 PHP 进程,所以得靠 PHP 原生方式读取 STDIN。直接用 fgets(STDIN) 或 readline() 就行,但要注意运行环境是否支持(比如 CI 环境下 STDIN 可能被重定向或关闭)。
常见错误现象:PHP Warning: fgets(): Read error on STDIN 或脚本卡住无响应——大概率是管道被关了,或在非交互终端(如 GitHub Actions)里强行调用 readline()。
-
readline()更友好(支持上下箭头、编辑),但 Windows 下默认不可用,需启用readline扩展;Linux/macOS 一般自带 -
fgets(STDIN)兼容性更好,但不支持行内编辑,输入后必须按回车 - 务必加
trim()清除换行符,否则后续字符串比较容易出错
如何让 Composer 脚本判断是否处于交互环境?
盲目读取输入会导致自动化流程失败。PHP 提供 stream_isatty(STDIN) 来检测当前 STDIN 是否连接到真实终端(TTY)。这是关键守门员。
使用场景:本地开发时提示确认,CI/CD 中自动跳过或走默认值。
- 如果
!stream_isatty(STDIN)为真,说明不是交互终端,应跳过提示、用预设默认值或抛出明确错误 - 别依赖
$_SERVER['TERM']或环境变量(如CI),它们可能缺失或被伪造;stream_isatty()是最直接可靠的判断依据 - 某些容器环境(如 Alpine)可能缺少
libc相关支持,stream_isatty()返回false,需额外 fallback
带默认值和选项的交互提示怎么写?
用户讨厌反复敲相同内容。用括号在提示末尾标注默认值(如 "Continue? [y/N]: "),再在代码里做逻辑映射即可,不需要第三方库。
参数差异:大小写敏感与否、空输入是否接受、是否允许缩写(如 y/yes)全由你控制。
- 用
strtolower(trim(fgets(STDIN)))统一处理输入,避免"Y"和"y"被当成不同选项 - 空输入直接
=== ''判断,对应默认行为;不要用empty(),它会把"0"也当空 - 示例逻辑:
$input === '' || $input === 'y' || $input === 'yes'→ 执行操作;否则中止
为什么有些 Composer 脚本提示没反应?
根本原因常是输出缓冲或进程继承问题。Composer 启动脚本时,若未显式开启输出缓冲,部分提示文字可能滞留在 buffer 里不显示,导致用户以为“卡住”。
性能 / 兼容性影响:这不是性能问题,而是 UX 断裂点——用户看不到提示,自然不会输入。
- 每次
echo提示后,立刻调用flush()和ob_flush()(如果启用了输出缓冲) - 更稳妥做法:在脚本开头加
if (function_exists('fflush')) { fflush(STDOUT); } - Windows CMD 下,
readline()可能完全失效,此时必须降级到fgets()+ 手动提示
交互提示真正难的不是读输入,而是预判执行环境、优雅降级、以及让用户清楚自己该干什么——哪怕只多一行 echo "Enter project name:";,也比静默等待强。











