2016-02-12 68 views
6

PHP manual指出,甲流用PHP打开://输入支持查找操作,并且可以多次读取的PHP 5.6的,但我不能让这行得通。下面的例子清楚地表明它不工作:PHP://输入只能在PHP读取一次5.6.16

<!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. 成功读取的

难道我做错了什么?

+2

随着例外的手册中的量和缺乏便携性,我建议干脆保存结果看完后的第一次。在此之后,你可以用结果寻求但是多你(和你没有将它保存到一个文件 - 'PHP:// memory'应该这样做) – h2ooooooo

+0

@ h2ooooooo谢谢你,我放弃了“固定的“这个问题最终以避免在未来的所有项目中阅读投入的计划结束,就像你所建议的那样。如果您将您的评论转换为答案,我会接受它。 – gseric

+0

不客气 - 给我一下,我会有一个答案。 – h2ooooooo

回答

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(); 
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。

+0

是的,我已经注意到它在这种情况下工作,但它不适合我,因为我使用“zend-diactoros”组件而不是直接访问流(我的示例被简化以避免混淆)。这个组件通过(字符串)$ request-> getBody()来抽象PHP输入流,所以我真的不想绕过zend-diactoros并手动打开流。 – gseric