PHP用curl实现AI流式响应需设CURLOPT_WRITEFUNCTION逐块处理、禁用缓冲,并配置Nginx proxy_buffering off及SSE解析逻辑。

PHP如何用curl接收并转发AI流式响应
PHP本身不支持真正的异步流式读取,但可以通过curl的CURLOPT_WRITEFUNCTION配合ob_flush()和flush()实现逐块接收、逐块输出。关键不是“等完整响应”,而是让cURL一收到数据就回调处理。
- 必须关闭
curl的默认缓冲:CURLOPT_RETURNTRANSFER => false,否则所有数据会攒在内存里 - 设置
CURLOPT_HTTPHEADER包含Accept: text/event-stream或application/json(取决于后端是否用SSE) - 若AI接口返回SSE(如
data: {...}),需在WRITEFUNCTION中手动解析data:行;若为纯JSON流(每行一个JSON对象),直接json_decode($chunk, true) - 每次写入后立即调用
ob_flush()和flush(),且确保Web服务器(如Nginx)未启用proxy_buffering on,否则会拦截流式输出
为什么file_get_contents()或json_decode(file_get_contents(...))完全不行
它会阻塞直到整个HTTP响应体下载完成,而AI流式响应可能持续数秒甚至更久——用户看到的是白屏等待,不是“实时”。更糟的是,大模型返回内容长时,PHP内存溢出风险极高。
-
file_get_contents()底层调用的是同步阻塞IO,无分块回调机制 - 即使加了
stream_context_create(['http'=>['timeout'=>0]]),也只是延长超时,不改变“全量加载”本质 - 某些托管环境(如共享主机)还会强制禁用
allow_url_fopen,直接报Warning: file_get_contents(): failed to open stream
如何在PHP中正确启用输出缓冲并避免Nginx/Apache截断
流式输出失效,八成是Web服务器或PHP配置在“默默缓存”。不能只改PHP代码,必须联动检查服务层。
- PHP端:开头加
ob_implicit_flush(true),并确认output_buffering = Off(php.ini)或运行时ini_set('output_buffering', 'Off') - Nginx端:location块中必须有
proxy_buffering off;和proxy_cache off;,否则它会攒满64KB才吐给浏览器 - Apache+mod_php:确认
php_flag output_buffering off已生效;若用FPM,还需检查fastcgi_buffering off(2.4.39+) - 浏览器端:用
fetch而非XMLHttpRequest,因后者对responseType: 'text'的流式支持不稳定;推荐用ReadableStream+getReader()逐段读
处理SSE格式时如何避免JSON解析失败和乱码
很多AI服务(如Ollama、vLLM)默认走SSE,但PHP回调拿到的是原始字节流,含换行、空行、注释行(: ping),直接json_decode()必错。
立即学习“PHP免费学习笔记(深入)”;
- 在
WRITEFUNCTION中先trim($data),跳过空行和以:开头的注释行 - 按行分割(
explode("\n", $data)),对每行判断是否以data:开头,再substr($line, 5)取值 - 注意SSE可能拼接多行
data:(如换行符在JSON内),实际应累积到双换行\n\n才解析整块 - 务必设
header('Content-Type: text/event-stream; charset=utf-8'),否则中文会变;且禁止任何echo非SSE格式内容(如调试var_dump)
proxy_buffering默认开启,以及SSE响应里隐藏的多行data:拼接规则。



















