php 8.5 不支持原生 websocket 服务,需依赖 workerman 等扩展;使用时须满足:workerman 4.1.0+、禁用 cli 模式 opcache、用 -d 后台启动,并注意端口暴露与进程管理。

PHP 8.5 本身不支持 WebSocket 服务
PHP 8.5 是语言核心,它没有内置 WebSocket 服务器能力,也没有 ext-websocket 扩展。你看到的“PHP WebSocket”几乎全是靠外部库或框架模拟/桥接实现的,不是 PHP 自己在 listen 和 upgrade。
常见误解是以为升级到 PHP 8.5 就能直接写 new WebSocketServer()——根本不存在这个类,也永远不会有。
- PHP 官方明确不计划加入原生 WebSocket 服务支持(只保留客户端基础如
fsockopen+ 手动协议处理) -
ext-swoole和ext-workerman都是独立扩展,和 PHP 版本号无强绑定,但需注意兼容性声明 - PHP 8.5 对协程、类型系统等有增强,但没改变 I/O 模型——还是阻塞式默认,得靠扩展破局
Workerman 在 PHP 8.5 下跑 WebSocket 的真实条件
Workerman 不依赖 PHP 协程,靠多进程 + select/epoll 实现异步,所以 PHP 8.5 完全可用,但要注意几个硬性前提:
- 必须用 Workerman 4.1.0+(旧版对 PHP 8.3+ 的 strict type 和反射变更兼容差)
- 不能开
opcache.enable_cli=1(Workerman 启动时动态加载类,Opcache 会缓存失败) - WebSocket 协议解析由 Workerman 内置完成,你只需处理
onMessage、onConnect等回调,不用碰Sec-WebSocket-Key或帧掩码 - 启动命令必须用
php start.php start -d,不能直接php start.php(前台运行会阻塞,且信号处理异常)
最小可运行示例(start.php):
立即学习“PHP免费学习笔记(深入)”;
use Workerman\Worker;
use Workerman\Lib\Timer;
require_once 'vendor/autoload.php';
$ws_worker = new Worker('websocket://0.0.0.0:2346');
$ws_worker->count = 4;
$ws_worker->onMessage = function ($connection, $data) {
$connection->send('echo: ' . $data);
};
Worker::runAll();
为什么别用 PHP 写 WebSocket 握手和帧解析
手动实现 WebSocket 协议(RFC 6455)在 PHP 里非常脆弱,尤其在 PHP 8.5 的严格类型和错误报告下,容易触发以下问题:
-
Undefined array key "Sec-WebSocket-Key":HTTP header 解析不规范,大小写敏感或代理改写导致 -
Uncaught ValueError: unpack(): Argument #2 ($format) must be a valid format string:WebSocket 帧解析时二进制长度计算错位,PHP 8.5 对unpack()格式校验更严 - 连接卡在
opening状态:没正确响应 101 Switching Protocols,或返回头缺失Connection: Upgrade - 文本帧被截断或乱码:没处理好 UTF-8 多字节边界,PHP 8.5 的
mb_函数默认行为更保守
Workerman、Swoole 这些方案的价值,就是把这部分脏活封死在 C 扩展或高性能 PHP 层里,你只管业务逻辑。
PHP 8.5 + Workerman 部署时最常漏掉的三件事
本地跑通不等于线上可用,尤其在 Docker 或 systemd 环境下:
- 没暴露容器端口:
docker run -p 2346:2346忘加,或防火墙没开2346(不是 80/443) - 没设
worker->name导致./start.php restart失效——Workerman 找不到旧进程 PID - 用
supervisor管理时,没设autorestart=true和startsecs=0(Workerman 启动快,startsecs 默认 1 秒会误判失败)
检查是否真在监听:ss -tlnp | grep ':2346',看到 php 进程才靠谱;只看到 LISTEN 但没进程名,大概率是启动命令没加 -d 或报错了静默退出。











