php://input can only be read once in PHP 5.6.16

匿名 (未验证) 提交于 2019-12-03 08:46:08

问题:

PHP manual states that a stream opened with php://input support seek operation and can be read multiple times as of PHP 5.6, but I can't make it work. The following example clearly shows it doesn't work:

<!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> 

Output:

First attempt: test_name=test_value Second attempt:  

php://input stream was

  1. successfully read
  2. successfully rewinded (fseek succeeded)
  3. unsuccessfully read

Am I doing something wrong?

回答1:

With the amount of exceptions and lack of portability using php://input I'd recommend you to read the stream and save it to another stream to avoid unexpected behaviour.

You can use php://memory in order to create a file-stream-like wrapper, which will give you all the same functionality that php://input should have without all of the annoying behaviour.

Example:

<?php  $inputHandle = fopen('php://memory', 'r+');  fwrite($inputHandle, file_get_contents('php://input'));  fseek($inputHandle, 0); 

Additionally you can create your own class to refer to this object consistently:

<?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);     } } 

Usage:

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

Usage (read all):

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


回答2:

Another approach might be to open the input stream each time instead of rewinding and seeking.

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

If the resource cost won't a problem.

Also there's file_get_contents

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

if you're sending json.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!