psr-15 不能直接用于 frankenphp worker 模式,因其请求/响应类型(*frankenphp.request/response)与 psr-7 接口不兼容,且无 sapi 生命周期、$_server 或自动 psr 容器支持,需胶水层桥接类型、处理不可变响应与单次读取 body 等差异。

FrankenPHP Worker 模式下,PSR-15 中间件不能直接挂载——它不走 PHP 的传统 SAPI 生命周期,也没有 $_SERVER 或原生 Request/Response 对象。你得写一层胶水,把 FrankenPHP 的 frankenphp.NewRequest() 和 frankenphp.NewResponse() 转成 PSR-7 标准对象,并把 PSR-15 的 handle() 结果再转回 FrankenPHP 可识别的响应结构。
为什么 PSR-15 不能直接用在 FrankenPHP Worker 中
FrankenPHP Worker 的请求处理入口是 Go 回调函数(如 frankenphp.WorkerHandler),它传入的是自定义的 *frankenphp.Request 和 *frankenphp.Response 类型,不是 PSR-7 的 ServerRequestInterface 或 ResponseInterface。框架层(如 Slim、Laravel 或自研管道)若只认 PSR-15,就会因类型不匹配而 panic 或静默失败。
- PSR-15 要求中间件实现
Psr\Http\Server\MiddlewareInterface,其process()方法必须接收ServerRequestInterface并返回ResponseInterface - FrankenPHP Worker 不提供这些接口实例,也不自动注入 PSR 容器
- 即使你手动 new 出 PSR-7 对象,
frankenphp.Response的写入逻辑(如WriteHeader()、Write())和 PSR-7 的不可变语义冲突——你不能靠withStatus()改状态,得直接调response.WriteHeader(404)
胶水层要做的三件事:Request / Response / Handler 绑定
核心就是桥接两端:输入端把 *frankenphp.Request 映射为 ServerRequestInterface,输出端把 PSR-15 返回的 ResponseInterface 写进 *frankenphp.Response,最后把整个中间件链包装成 frankenphp.WorkerHandler 可调用的函数。
- Request 转换:用
frankenphp.Request.URL、.Method、.Header、.Body构造Psr\Http\Message\ServerRequestInterface实例(推荐用guzzlehttp/psr7的ServerRequest::fromGlobals()不可用,得手写或用nyholm/psr7的工厂) - Response 写回:拿到 PSR-7
ResponseInterface后,遍历getHeaders()调response.Header().Set(),调response.WriteHeader(statusCode),再用getBody()->getContents()写入response.Write() - Handler 封装:把你的 PSR-15
RequestHandlerInterface(比如 Slim 的App)包进一个闭包,作为frankenphp.WorkerHandler的参数传给frankenphp.SetWorkerHandler()
常见坑:Body 读取一次就 EOF、Header 写入顺序、状态码覆盖
FrankenPHP 的 *frankenphp.Request.Body 是单次可读的 io.ReadCloser,PSR-7 的 ServerRequestInterface 默认也要求 body 可 rewind——但你没法 reset。一旦 PSR-7 实现(如 nyholm/psr7)调了 getParsedBody() 或 getBody()->getContents(),原始 body 就空了,后续中间件再读会得到空内容。
- 解决办法:在 Request 转换阶段,先
io.ReadAll(r.Body),然后用newStream()或newBufferedBody()创建可重复读的 body 实例 - Header 写入必须在
WriteHeader()之前完成,否则会被忽略;FrankenPHP 不允许先 Write 再 SetHeader - PSR-7 响应的状态码可能被中间件链多次修改,但
frankenphp.Response.WriteHeader()只能调一次——务必在最终响应确定后再写,别在中间件里提前触发 - 不要依赖
$_SERVER或php_sapi_name(),Worker 模式下它们不可靠;所有环境信息必须从frankenphp.Request显式提取
最易被忽略的是 body 生命周期管理:FrankenPHP 不帮你缓存原始请求体,PSR-7 库也不自动 clone,一读全没。胶水层必须在构造 ServerRequestInterface 时就完成一次完整读取并封装为可复用流,否则下游中间件拿不到 POST 数据或 JSON body。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











