是否可以在开始读取之前知道 php://input 是否包含数据


Is it possible to know whether php://input contains data before starting reading it?

根据文档,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

对我来说,这表明它是一个老式的只读流。您无法查找和测试它是文件的结尾还是长度。