php://input在PHP5.6.16中只能读取一次
php://input can only be read once in PHP 5.6.16
PHP manual 表示使用 php://input 打开的流支持 seek 操作并且可以从 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://输入流是
- 读取成功
- 倒带成功(fseek 成功)
- 读取失败
我是不是做错了什么?
另一种方法可能是每次都打开输入流,而不是倒带和查找。
$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。
使用 php://input
时出现大量异常且缺乏可移植性,我建议您阅读流并将其保存到另一个流中以避免意外行为。
您可以使用 php://memory
来创建类似文件流的包装器,这将为您提供 php://input
应该 的所有相同功能没有所有烦人的行为。
示例:
<?php
$inputHandle = fopen('php://memory', 'r+');
fwrite($inputHandle, file_get_contents('php://input'));
fseek($inputHandle, 0);
此外,您可以创建自己的 class 以始终如一地引用此对象:
<?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();
PHP manual 表示使用 php://input 打开的流支持 seek 操作并且可以从 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://输入流是
- 读取成功
- 倒带成功(fseek 成功)
- 读取失败
我是不是做错了什么?
另一种方法可能是每次都打开输入流,而不是倒带和查找。
$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。
使用 php://input
时出现大量异常且缺乏可移植性,我建议您阅读流并将其保存到另一个流中以避免意外行为。
您可以使用 php://memory
来创建类似文件流的包装器,这将为您提供 php://input
应该 的所有相同功能没有所有烦人的行为。
示例:
<?php
$inputHandle = fopen('php://memory', 'r+');
fwrite($inputHandle, file_get_contents('php://input'));
fseek($inputHandle, 0);
此外,您可以创建自己的 class 以始终如一地引用此对象:
<?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();