PHP CLI 默认不缓冲输出,但系统级 stdio 缓冲(行缓存/全缓存)导致 echo 等看似不实时;解决方法是每次输出后调用 fflush(STDOUT),尤其在管道或重定向时必需。

PHP 在 CLI 环境下默认**不缓冲输出**,所以 echo、print、var_dump 等函数会立即打印到终端——但“实时输出”常指「逐行/逐字强制刷新」,这时反而容易出问题,因为 CLI 的 stdout 默认是行缓冲(line-buffered)或全缓冲(fully buffered),尤其在管道、重定向或某些 shell 下。
CLI 下为什么 echo 看起来没实时?
不是 PHP 不输出,而是输出被系统级 stdio 缓冲了。典型现象:
- 运行
php script.php时有延迟,最后一口气全出来 - 用
php script.php | cat或重定向到文件后完全卡住 -
sleep(1)前后的echo "step 1\n"没立刻显示
根本原因是:当 stdout 连接到终端(tty)时,glibc 通常启用行缓冲;一旦 stdout 被重定向(如管道、文件),就自动切换为全缓冲——此时必须手动刷新。
如何强制 CLI 实时刷新输出?
核心是绕过 stdio 缓冲,直接控制输出流:
立即学习“PHP免费学习笔记(深入)”;
- 调用
fflush(STDOUT)—— 最常用、最可靠,每次输出后加一句即可 - 禁用输出缓冲:
ob_end_flush()+ob_implicit_flush(true)(但 CLI 下ob_*作用有限,不推荐主用) - 用
fwrite(STDOUT, "msg")替代echo,再跟fflush(STDOUT) - 启动时加
-d output_buffering=0(对 CLI 无效,该 ini 只影响 Web SAPI)
示例:
while ($i < 5) {
echo "Tick $i\n";
fflush(STDOUT); // 关键
sleep(1);
$i++;
}管道/重定向场景下的陷阱
当 CLI 脚本被管道消费(如 php long.php | grep "done")或重定向(php run.php > log.txt),stdout 缓冲模式会从行缓冲变为全缓冲,\n 不再触发自动刷新。此时仅靠换行符没用,fflush(STDOUT) 成为必需。
- 别依赖
set_time_limit(0)或ignore_user_abort(true)—— 它们对 CLI 缓冲无效 - 避免用
ob_start()包裹长循环输出,否则缓冲层叠更难调试 - 如果脚本要同时支持交互式运行和管道消费,建议统一加
fflush(STDOUT),不区分环境
Windows CLI 的特殊注意点
Windows cmd/powershell 对 stdout 缓冲行为更不稳定:
- 某些旧版 PHP(如 7.2 以下)在 Windows 上
fflush(STDOUT)可能无效 - 可尝试
ob_flush()+flush()组合(虽文档说 CLI 忽略,但部分环境有效) - 终极方案:用
file_put_contents('php://stdout', "msg\n", FILE_APPEND | LOCK_EX),它绕过 stdio,强制写入
不过现代 PHP(8.0+)在 Windows 上 fflush(STDOUT) 已基本可靠,优先用它。
真正容易被忽略的点是:你以为在终端里跑就没缓冲,结果一加管道就卡死——而修复只需一行 fflush(STDOUT)。别猜缓冲策略,所有长耗时 CLI 输出逻辑,都该默认加上它。











