PSR-7是一套接口契约而非具体类库,直接new Request()违反依赖倒置原则,导致测试困难、实现锁定、DI失效;必须通过PSR-17工厂创建不可变消息对象,并由中间件预解析输入。

PSR-7 不是“一个类”或“一个库”,而是一套接口契约;直接 new 具体实现类(比如 Laminas\Diactoros\Request)会锁死代码,破坏可测试性与框架迁移能力。
为什么不能直接 new Request()?
硬编码实例化违反了依赖倒置原则,导致:
- 单元测试必须
mock具体类,而非抽象接口Psr\Http\Message\RequestInterface - 切换 PSR-7 实现(如从
laminas/diactoros换成guzzlehttp/psr7)时,所有new调用都得手动重写 - DI 容器无法统一管理对象生命周期,中间件、路由等组件难以解耦
必须用 PSR-17 工厂创建消息对象
PSR-17 是 PSR-7 的配套标准,定义了工厂接口,用于解耦创建逻辑。Laminas Diactoros 同时实现了 PSR-7 和 PSR-17,但工厂类不会自动注册——你得显式实例化:
-
new Laminas\Diactoros\RequestFactory()→ 实现Psr\Http\Message\RequestFactoryInterface -
new Laminas\Diactoros\ResponseFactory()→ 实现Psr\Http\Message\ResponseFactoryInterface -
new Laminas\Diactoros\UriFactory()→ 实现Psr\Http\Message\UriFactoryInterface -
new Laminas\Diactoros\StreamFactory()→ 实现Psr\Http\Message\StreamFactoryInterface
示例:
立即学习“PHP免费学习笔记(深入)”;
use Laminas\Diactoros\RequestFactory;
use Laminas\Diactoros\UriFactory;
use Laminas\Diactoros\StreamFactory;
$requestFactory = new RequestFactory();
$uriFactory = new UriFactory();
$streamFactory = new StreamFactory();
$uri = $uriFactory->createUri('https://api.example.com');
$body = $streamFactory->createStream('{"id":123}');
$request = $requestFactory->createRequest('POST', $uri)
->withHeader('Content-Type', 'application/json')
->withBody($body);
MessageInterface 是所有消息的起点
所有请求、响应、上传文件、URI 等对象都继承自 MessageInterface,它只管三件事:协议版本、头(headers)、体(body)。关键方法行为必须牢记:
-
getHeader($name)返回数组(即使只有一个值),getHeaderLine($name)才返回逗号拼接字符串 -
withHeader($name, $value)是覆盖写入,withAddedHeader($name, $value)是追加(保留原值) -
getBody()返回StreamInterface实例,不是字符串;要读内容得调getContents()或__toString() - 所有
withXxx()方法都返回新对象,原对象不可变——这是设计核心,不是 bug
ServerRequestInterface 专用于服务端接收的请求
它比 RequestInterface 多出服务器上下文数据,常见误用点:
-
getQueryParams()不等于$_GET自动解析——它返回的是已解析的数组,但需由框架或中间件预先调用parse_str()填充;若未填充,返回空数组 -
getParsedBody()同理,不自动解析php://input,得靠上层解析并用withParsedBody()注入 -
getUploadedFiles()返回UploadedFileInterface数组,但原始$_FILES结构需由工厂或中间件转换,不能直接传入 -
getAttribute($name)和withAttribute()是中间件间传递数据的唯一合规方式,别再用全局变量或静态属性
真正容易被忽略的是:PSR-7 本身不处理输入解析,它只提供容器和契约。解析逻辑(如 JSON body 解析、form-data 处理)必须由框架或中间件在构造 ServerRequestInterface 实例前完成——这点不理解,就永远卡在“为什么 getParsedBody() 是 null”。



















