最简实时推送需在.frankenphp.yaml中显式配置eventsource: true路由,php脚本设text/event-stream响应头、禁用缓冲,并循环调用flush()和ob_flush()推送;前端用eventsource监听message事件。

用 FrankenPHP 的 eventsource 路由写最简实时推送
FrankenPHP 本身不内置 WebSocket 服务,但原生支持 Server-Sent Events(SSE),这是实现轻量级实时推送最直接、零依赖的方式。关键不是“怎么装”,而是怎么让 PHP 脚本持续输出并被 FrankenPHP 正确识别为流式响应。
必须确保:frankenphp: true 在 .frankenphp.yaml 中启用,且路由显式声明为 eventsource 类型 —— 否则请求会超时或被缓冲,根本看不到“实时”效果。
-
header('Content-Type: text/event-stream')和header('Cache-Control: no-cache')缺一不可,漏掉任一都会导致浏览器拒绝解析 - 每次发送数据后必须调用
flush()和ob_flush(),否则 PHP 输出缓冲区会卡住消息 - 不要用
sleep(1)模拟推送;真实场景建议用stream_select()或监听文件/信号,避免阻塞整个 worker
示例脚本 push.php:
<?php header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no'); // 关键:禁用 Nginx 级缓冲
$counter = 0;
while (true) {
if (connection_aborted()) break;
echo "data: {"count": $counter, "time": " . time() . "}\n\n";
flush();
ob_flush();
$counter++;
usleep(1000000); // 1 秒间隔
}
为什么 .frankenphp.yaml 必须显式配置 eventsource
FrankenPHP 默认把所有 PHP 脚本当普通 CGI 处理,响应完成后才发给客户端。只有在路由配置中标明 eventsource: true,它才会绕过默认生命周期,保持连接打开、逐块转发输出。
常见错误是只写了 PHP 脚本却忘了配 YAML,结果浏览器一直 pending,控制台也无错误 —— 因为 HTTP 状态码是 200,只是内容没出来。
- 路径匹配必须精确,比如
push.php对应的路由不能写成/push/*,而应是/push.php -
eventsource配置下不支持index自动映射,/push/访问会 404,必须带完整文件名 - 开发时可加
log_level: debug查看 FrankenPHP 是否识别到 eventsource 模式(日志里会出现sse handler字样)
前端用 EventSource 接收时的兼容性注意点
Chrome/Firefox/Safari 原生支持 EventSource,但 Edge 旧版本(≤18)不支持,且所有浏览器对连接中断后的自动重连策略不同 —— FrankenPHP 不干预这部分,全靠前端控制。
- 不要依赖
onerror判断断连:它可能在连接建立前就触发(如 CORS 失败),应结合readyState值判断 - 避免在
onmessage里做耗时操作,否则会阻塞后续消息解析;复杂逻辑建议postMessage给 Web Worker - 如果后端推送频率高(如 >5 条/秒),需在 PHP 端加限流,否则浏览器 EventSource 缓冲区溢出会静默丢消息
最小可行前端代码:
const es = new EventSource('/push.php');
es.onmessage = e => console.log(JSON.parse(e.data));
es.onerror = () => console.warn('SSE connection error', es.readyState);
调试时最容易忽略的三个点
90% 的“推送没反应”问题不出在 PHP 逻辑,而出在中间层拦截或配置遗漏。
- 反向代理(如 Caddy/Nginx)默认缓冲 SSE 响应,必须显式关闭:
proxy_buffering off或proxy_cache off - PHP 的
output_buffering若设为On(尤其某些 Docker 镜像默认开启),会导致flush()失效,应设为Off或0 - 浏览器开发者工具的 Network 面板里,SSE 请求的 “Size” 显示为 “(pending)” 是正常现象;要确认是否收到数据,得看 “Response” 标签页实时滚动内容,而不是看状态栏
真正卡住的地方往往不在 PHP 循环里,而在你没看见的缓冲层或配置开关上。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











