根据文档,php://input
并不总是包含HTTP请求正文。
我有一些代码总是在脚本启动时打开流,以使其可供用户应用程序使用:
$stream = fopen('php://input', 'rb');
如果用户应用程序尝试从空流中读取,则在Content-Length > 0
时,我希望我的库抛出异常:
请求正文不可用。
我的问题是:
- 如果流为空,则
feof()
返回false
而尚未调用fread()
; - 我不能
fread()
1 个字节然后fseek()
,因为此流不支持搜索; - 我无法打开另一个
php://input
并尝试事先从中读取,因为我们不能依赖此流多次可用; - 出于性能原因,我不想在将手交给用户应用程序之前盲目地将
php://input
复制到php://temp
。
因此,是否可以判断php://input
是否在fopen()
之后,但在任何fread()
之前包含数据?
我找到的解决方案是创建一个表示请求正文的类,并从该类读取而不是直接读取php://input
:
class RequestBody
{
protected $firstByte;
protected $isEmpty;
public function __construct($stream)
{
$this->firstByte = fread($stream, 1);
$this->isEmpty = ($this->firstByte === '');
// ...
}
public function isEmpty()
{
return $this->isEmpty;
}
public function read($length)
{
// ...
}
}
$body = new RequestBody(fopen('php://input', 'rb'));
类构造函数读取流的第一个字节,存储它以供以后使用,并立即知道流是否为空。
read()
通过在第一次调用时返回第一个字节 + $length - 1
个字节来隐藏这种复杂性。
"用 php://input 打开的溪流只能读取一次;流不支持寻道操作。
http://php.net/manual/en/wrappers.php.php
对我来说,这表明它是一个老式的只读流。您无法查找和测试它是文件的结尾还是长度。