php: // ввод может быть прочитан только один раз в PHP 5.6.16

В руководстве PHP указано, что поток, открытый с помощью операции поиска поддержки 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. безуспешно читать

Я делаю что-то неправильно?

    Учитывая количество исключений и отсутствие переносимости с использованием 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(); 

    Другой подход может заключаться в том, чтобы каждый раз открывать входной поток, а не перематывать и искать.

     $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.