【PHP】如何关闭buffer实时输出内容到前端

本文介绍了如何在PHP和Nginx环境中关闭输出缓冲,以实现实时响应,包括使用ob_end_flush,ob_end_clean,ob_implicit_flush以及通过调整Nginx配置或HTTP头来控制缓冲。还提供了PHPcurl调用实时接口的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

默认情况下,我们在PHP里使用echo等函数输出的内容,是不会马上发送给前端的,原因是有 buffer 的存在,buffer又分两处,一处是PHP本身的buffer,另一处是Nginx的buffer。只有当buffer满了之后,内容才会发送。

但有时候我们会希望输出的内容可以马上发送给前端,例如类似ChatGPT之类的应用,回答都是一个一个字的实时输出的,给用户良好的体验。

那么,怎么关闭 PHP 和 Nginx 的 buffer 呢?

环境

Nginx 1.19
PHP 7.4

解决方法

一、PHP的buffer

PHP里有两个函数可以关闭buffer缓冲,一个是ob_end_flush,一个是ob_end_clean,前者是输出缓冲区内容后关闭缓冲区,后者是销毁缓冲区内容直接关闭。

但即使我们关闭了 PHP 的缓冲区,每次输出完内容也还是要手动 flush 的,例如:

echo 'Hello World';
flush();

每次echo完都要调用一次flush函数,太麻烦了,此时我们可以使用ob_implicit_flush函数来解决这个问题。

二、Nginx的buffer

Nginx有两种方法关闭缓冲区,第一种是改Nginx的配置文件:
在这里插入图片描述
加上图中红色框的配置指令就可以。

这种改配置文件的方法影响范围会比较大,会导致所有的PHP请求都会关闭缓冲区,不太推荐。

第二种方法是在 PHP 里输出 HTTP 响应头,只要在响应头里加上一个X-Accel-Buffering: no,Nginx看到此响应头就会放弃使用buffer缓冲。由于这种方法是通过代码来控制,所以影响范围我们可以自由操控,推荐使用。

PHP代码示例

function stream()
{
    // 如果缓冲区没有开启,直接调用ob_end_clean()会报错的,要先判断缓冲区有没有开启
    // 如果ob_get_contents()不是返回false,说明有开启缓冲区(ob_start())
    $buf = ob_get_contents();
    if ($buf !== false) {
        // 输出header前,不能有任何输出内容,否则会报错,所以缓冲区里的内容要全部清空
        ob_end_clean();
    }
    ob_implicit_flush(); // 每次输出后都自动flush,这样就不需要咱们手动flush了

    // 输出header,让Nginx不要使用buffer
    header('X-Accel-Buffering: no');

    // 每隔一秒输出一个数字
    for ($i = 0; $i < 10; $i++) {
        echo "$i\n";
        sleep(1);
    }
}

stream();

在 PowerShell 命令行中访问此页面,可以看到数字会一个一个的实时显示出来:
在这里插入图片描述

为什么要在命令行中访问?
答:因为有部分前端程序也是有自己的缓冲区的,即使后端实时输出内容了,前端也不会马上显示出来,为了避免这种问题,使用命令行来访问就很适合了。

PHP curl调用

如果用 PHP 的 curl_exec 函数调用以上实时输出的接口,默认情况下是要等全部内容输出完毕后,才会得到响应结果的,比如下面这段代码:

// 实时输出内容的第三方接口,每隔1秒输出一个数字,输出10个,全部输出完需要10秒时间
$url = "http://www.demo.com/stream";

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($ch); // 要等待10秒,才能获取到第三方接口的全部输出内容,然后一次性var_dump出来

var_dump($result);

那么怎样才能实时获取第三方接口的输出内容呢?可以使用 CURLOPT_WRITEFUNCTION 配置项:

function write_fn($ch, $data)
{
    var_dump($data); // 实时获取接口输出内容,并打印
    return strlen($data);
}


$url = "http://www.demo.com/stream";

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'write_fn'); // 设置了这个之后,curl_exec会返回bool值,而不是响应结果字符串(即使CURLOPT_RETURNTRANSFER设置为了1)
$result = curl_exec($ch);

if ($result === false) {
    var_dump('调用接口失败');
} else {
    var_dump('调用接口成功');
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值