$response->end()立即终止http响应,后续write/header/cookie均无效;write仅分段发送数据,需手动end结束,适用于流式输出和sse等场景。

response->end 会终止响应,write 只是发送一段数据
end 是终结性操作:调用后 HTTP 响应立即结束,底层会发送一个 Content-Length(或 Transfer-Encoding: chunked 结束标记),后续再调用 write、header、cookie 都无效,且不会报错——只是静默丢弃。
write 是分段式写入:它只发一块数据,不关闭连接,也不设置状态码或响应头(除非你提前设好),适合流式输出场景,比如实时日志、大文件分块、SSE(Server-Sent Events)。
常见错误现象:$response->write("a"); $response->end("b"); 实际只输出 b;反过来写则 a 能发出,b 会被忽略。
write 后必须手动 end,否则客户端可能一直等待
HTTP 协议要求响应有明确的结束信号。write 发送的是 HTTP Chunk,但没有 0\r\n\r\n 表示终结,浏览器或 curl 就卡在 pending 状态,直到超时。
使用场景举例:
- 动态生成大 CSV 文件,边查库边
write,最后end() - 协程中调用多个异步 I/O(如 Redis + MySQL),每完成一项就
write进度,最后end总结果 - 配合
header("Content-Type: text/event-stream")实现 SSE
注意:write 的单次数据上限是 2MB,超长会直接失败返回 false,而 end 没有这个限制(但受内存和超时控制)。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
header 和 cookie 必须在 end 之前设置,write 不影响它们的时机
header 和 cookie 的生效前提是响应尚未发送。只要没调用 end,哪怕已经 write 过多次,依然可以安全设置这些元信息。
但要注意顺序逻辑:
-
$response->header("X-Status", "processing"); $response->write("start");✅ 有效 -
$response->write("start"); $response->header("X-Status", "done");❌ header 失效(已发 chunk,无法追加 header) -
$response->end("done"); $response->header("X-Status", "oops");❌ 完全无效
所以判断依据不是“有没有 write”,而是“有没有 end”——isEndResponse() 才是唯一可靠的检查方式。
end("") 和 end() 行为不同,空字符串会触发 Content-Length: 0
end()(无参数)等价于 end(""),都会发送空响应体,状态码默认 200,Content-Length: 0。
但如果你先 write 过内容,再 end(),底层会自动补一个长度为 0 的 chunk 表示结束;而 end("final") 则会覆盖之前所有 write,只发 "final"。
容易被忽略的点:
- 框架封装层(如 ThinkPHP Swoole 扩展)可能在中间件里悄悄调用了
end,导致你在控制器里write无效 -
sendfile是特殊 case:它内部会自动调用end,不能和write混用 - 协程环境下,
write是非阻塞的,但end仍是同步终态操作










