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

Руководство по PHP утверждает, что поток, открытый с помощью php: // input, поддерживает операцию поиска и может быть прочитан несколько раз, начиная с 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. неудачно прочитано

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

6

Решение

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

1

По вопросам рекламы [email protected]