PHP 输出流经四层缓冲:
echo/print → PHP output_buffering → Server(Apache/Nginx)缓冲 → Browser缓冲 → 显示
output_buffering (php.ini) 或 ob_start() 控制| 配置 | 行为 | 典型场景 |
|---|---|---|
output_buffering = 4096 (默认 On) | 4KB 缓冲区,满或脚本结束才刷新 | 生产环境默认,减少系统调用 |
output_buffering = 0 (Off) | 无 PHP 缓冲,直接进 Server 缓冲 | 需实时输出 (长轮询、流式下载) |
ob_start() 显式开启 | 无限大缓冲区,脚本结束或 ob_end_flush() 才输出 | 模板引擎、静态化捕获、头信息修正 |
for ($i=0;$i<5;$i++) {
echo $i . '
';
sleep(1);
}
// 结果:等 5 秒后一次性输出 0-4 (数据未满 4KB)
ini_set('output_buffering', '0'); // 或 php.ini 设为 0
echo str_repeat(' ', 1024); // 填满浏览器 1KB 阈值
for ($i=0;$i<5;$i++) {
echo $i . '
';
flush(); // 刷新 PHP → Server → Browser
sleep(1);
}
// 结果:每秒输出一个数字 (实时流式)
for ($i=0;$i<5;$i++) {
echo file_get_contents('large.txt') . $i . '
'; // >4KB
sleep(2);
}
// 结果:每次循环输出一次 (缓冲区满自动刷新)
ob_start();
for ($i=0;$i<5;$i++) {
echo file_get_contents('large.txt') . $i . '
';
sleep(2);
}
// 结果:5 秒后一次性输出所有 (ob_start 分配巨大缓冲区)
| 函数 | 作用 | 返回/影响 |
|---|---|---|
ob_start(callable $callback?) | 开启输出缓冲,可选回调处理内容 | 成功返回 true |
ob_get_contents() | 获取当前缓冲区内容 | 字符串,需在 ob_end_clean() 前调用 |
ob_end_flush() | 发送缓冲内容并关闭缓冲 | 输出内容,保留缓冲数据 |
ob_end_clean() | 丢弃缓冲内容并关闭缓冲 | 无输出,清空缓冲 |
ob_flush() | 刷新 PHP 缓冲 → Server 缓冲 | 不关闭缓冲,可重复调用 |
flush() | 刷新 Server 缓冲 → Browser | 系统级刷新,配合 ob_flush() 实时输出 |
ob_implicit_flush(true) | 开启隐式刷新,每次输出自动 flush | 免显式调用 flush() |
ob_get_clean() | ob_get_contents() + ob_end_clean() | 获取并清空,常用模板捕获 |
ob_start(function($buffer) { return strtoupper($buffer); }); // 外层: 大写
echo 'hello ';
ob_start(function($buffer) { return '[' . $buffer . ']'; }); // 内层: 加括号
echo 'world';
ob_end_flush(); // 内层输出: [world]
ob_end_flush(); // 外层输出: HELLO [WORLD]
嵌套按栈序执行,回调函数链式过滤内容。
原因:header() 必须在任何输出前调用。开启 ob_start() 后,所有 echo 先入 PHP 缓冲,header() 直接发送,脚本结束时缓冲内容随头信息一起发送,顺序自动修正。
ob_start(); // 文件最开头
echo '内容...';
header('Location: /login.php'); // 即使前面有 echo 也不会报错
ob_end_flush();
ob_start();
include 'template.php'; // 模板中混合 PHP/HTML
$html = ob_get_clean(); // 获取渲染结果
file_put_contents('static/page.html', $html); // 生成静态文件
output_buffering = Off;5.3+ 默认 4096 (On)ini_set('output_buffering', '0')ob_start() 嵌套过深,增加内存与复杂度ob_gzhandler 回调可自动 Gzip 压缩输出:ob_start('ob_gzhandler')