php://输入只能在PHP 5.6.16中读取一次

时间:2016-02-12 11:43:02

标签: php php-stream-wrappers

PHP manual表示使用php://输入支持查找操作打开的流,从PHP 5.6开始可以多次读取,但我无法使其工作。以下示例清楚地表明它不起作用:

<!DOCTYPE html>
<html>
<body>
<form method="post">
<input type="hidden" name="test_name" value="test_value">
<input type="submit">
</form>
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST')
{
    $input = fopen('php://input', 'r');
    echo 'First attempt: ' . fread($input, 1024) . '<br>';
    if (fseek($input, 0) != 0)
        exit('Seek failed');
    echo 'Second attempt: ' . fread($input, 1024) . '<br>';
}
?>
</body>
</html>

输出:

First attempt: test_name=test_value
Second attempt: 

php://输入流是

  1. 已成功阅读
  2. 成功回归(fseek成功)
  3. 未成功阅读
  4. 我做错了吗?

2 个答案:

答案 0 :(得分:1)

另一种方法可能是每次打开输入流而不是倒带和搜索。

$input = fopen('php://input', 'r');
echo 'First attempt: ' . fread($input, 1024) . '<br>';
$input2 = fopen('php://input', 'r');
echo 'Second attempt: ' . fread($input2, 1024) . '<br>';

如果资源成本不成问题。

还有file_get_contents

$input = file_get_contents("php://input");
$input = json_decode($input, TRUE);

如果你要发送json。

答案 1 :(得分:1)

使用php://input的异常数量和缺乏可移植性我建议您阅读流并将其保存到另一个流中以避免意外行为。

您可以使用php://memory来创建类似文件流的包装器,它将为您提供php://input 应该所具有的所有相同功能讨厌的行为。

示例:

<?php

$inputHandle = fopen('php://memory', 'r+');

fwrite($inputHandle, file_get_contents('php://input'));

fseek($inputHandle, 0);

此外,您可以创建自己的类以始终如一地引用此对象:

<?php

class InputReader {
    private static $instance;

    /**
     * Factory for InputReader
     *
     * @param string $inputContents
     *
     * @return InputReader
     */
    public static function instance($inputContents = null) {
        if (self::$instance === null) {
            self::$instance = new InputReader($inputContents);
        }

        return self::$instance;
    }

    protected $handle;

    /**
     * InputReader constructor.
     *
     * @param string $inputContents
     */
    public function __construct($inputContents = null) {
        // Open up a new memory handle
        $this->handle = fopen('php://memory', 'r+');

        // If we haven't specified the input contents (in case you're reading it from somewhere else like a framework), then we'll read it again
        if ($inputContents === null) {
            $inputContents = file_get_contents('php://input');
        }

        // Write all the contents of php://input to our memory handle
        fwrite($this->handle, $inputContents);

        // Seek back to the start if we're reading anything
        fseek($this->handle, 0);
    }

    public function getHandle() {
        return $this->handle;
    }

    /**
     * Wrapper for fseek
     *
     * @param int $offset
     * @param int $whence
     *
     * @return InputReader
     *
     * @throws \Exception
     */
    public function seek($offset, $whence = SEEK_SET) {
        if (fseek($this->handle, $offset, $whence) !== 0) {
            throw new \Exception('Could not use fseek on memory handle');
        }

        return $this;
    }

    public function read($length) {
        $read = fread($this->handle, $length);

        if ($read === false) {
            throw new \Exception('Could not use fread on memory handle');
        }

        return $read;
    }

    public function readAll($buffer = 8192) {
        $reader = '';

        $this->seek(0); // make sure we start by seeking to offset 0

        while (!$this->eof()) {
            $reader .= $this->read($buffer);
        }

        return $reader;
    }

    public function eof() {
        return feof($this->handle);
    }
}

<强>用法:

$first1024Bytes = InputReader::instance()->seek(0)->read(1024);
$next1024Bytes = InputReader::instance()->read(1024);

用法(全部阅读):

$phpInput = InputReader::instance()->readAll();