В руководстве 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: // входной поток был
Я делаю что-то неправильно?
Учитывая количество исключений и отсутствие переносимости с использованием 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.