Почему php: // input может быть прочитан несколько раз, несмотря на то, что в документации сказано иначе? - PullRequest
17 голосов
/ 24 июня 2010

В документации PHP говорится , что php://input может быть прочитан только один раз.

В моем приложении мне нужно прочитать его дважды, один раз для целей аутентификации и один раз для фактической обработки контента, и обе функции обрабатываются разными независимыми модулями. Сумасшедшая вещь: это работает .

Могу ли я рассчитывать на эту работу везде или это случайность в моей версии PHP (5.2.10)? Единственная документация, которую я могу найти по этому поводу, это та, которая утверждает, что она не должна работать, без упоминания ограничения версии.


После догадки Денниса я сделал этот тест:

$in = fopen('php://input', 'r');
echo fread($in, 1024) . "\n";
fseek($in, 0);
echo fread($in, 1024) . "\n";
fclose($in);
echo file_get_contents('php://input') . "\n";

Керлинг:

$ curl http://localhost:8888/tests/test.php -d "This is a test"
This is a test

This is a test

Очевидно, это ограничено одним чтением на открытую ручку .


Немного больше раскопок показало, что действительно php://input может быть прочитано только один раз, для запросов PUT . В приведенном выше примере используется запрос POST.

Ответы [ 2 ]

23 голосов
/ 04 августа 2010

Небольшая проверка исходного кода дает ответы.

Во-первых, да, вы ограничены одним чтением на дескриптор, поскольку базовый поток не реализует обработчик seek:

php_stream_ops php_stream_input_ops = {
    php_stream_input_write,
    /* ... */
    "Input",
    NULL, /* seek */
    /* ... */
};

Во-вторых, обработчик чтения имеет два различных поведения в зависимости от того, были ли «POST-данные» прочитаны и сохранены в SG(request_info).raw_post_data.

if (SG(request_info).raw_post_data) {
    read_bytes = SG(request_info).raw_post_data_length - *position;
    /* ...*/
    if (read_bytes) {
        memcpy(buf, SG(request_info).raw_post_data + *position, read_bytes);
    }
} else if (sapi_module.read_post) {
    read_bytes = sapi_module.read_post(buf, count TSRMLS_CC);
    /* ... */
} else {
    stream->eof = 1;
}

Итак, у нас есть три возможности:

  1. Данные тела запроса уже прочитаны и сохранены в SG(request_info).raw_post_data. В этом случае, поскольку данные хранятся, мы можем открыть и прочитать несколько дескрипторов для php://input.
  2. Данные тела запроса были прочитаны, но их содержимое нигде не было сохранено. php://input не может дать нам ничего.
  3. Данные запроса еще не прочитаны. Это означает, что мы можем открыть php://input и прочитать его только один раз.

ПРИМЕЧАНИЕ. Ниже приведено поведение по умолчанию. Различные SAPI или дополнительные расширения могут изменить это поведение.

В случае POST-запросов PHP определяет другое устройство чтения POST и обработчик POST в зависимости от типа содержимого.

Случай 1. Это происходит, когда у нас есть запрос POST:

  • С типом содержимого application/x-www-form-encoded. sapi_activate обнаруживает запрос POST с типом контента и вызывает sapi_read_post_data. Это определяет тип контента и определяет пару читатель / обработчик POST. Программа чтения POST - sapi_read_standard_form_data, которая вызывается немедленно и просто копирует тело запроса в SG(request_info).post_data. Затем вызывается программа чтения сообщений по умолчанию php_default_post_reader, которая заполняет $HTTP_RAW_POST_DATA, если задан параметр ini always_populate_post_data, а затем копирует SG(request_info).post_data в SG(request_info).raw_post_data и очищает первый. Вызов обработчику здесь не имеет значения и откладывается до тех пор, пока не будут построены суперглобальные объекты (что может не произойти, если JIT активирован и суперглобальные элементы не используются).
  • С нераспознанным или несуществующим типом контента . В этом случае нет определенного считывателя и обработчика POST. Оба случая заканчиваются на php_default_post_reader без чтения данных. Так как это запрос POST и пары читателей / обработчиков нет, будет вызван sapi_read_standard_form_data. Это та же функция, что и у обработчика чтения типа содержимого application/x-www-form-encoded, поэтому все данные проглатываются до SG(request_info).post_data. Единственное отличие от этого состоит в том, что $HTTP_RAW_POST_DATA всегда заполнен (независимо от значения always_populate_post_data) и нет обработчика для построения суперглобальных элементов.

Случай 2. Это происходит, когда у нас есть запрос формы с типом содержимого "multipart / form-data". Считыватель POST NULL, поэтому обработчик rfc1867_post_handler действует как смешанный reader/handler. В фазе sapi_activate данные вообще не читаются. Функция sapi_handle_post в конечном итоге вызывается на более позднем этапе, который, в свою очередь, вызывает обработчик POST. rfc1867_post_handler читает данные запроса, заполняет POST и FILES, но ничего не оставляет в SG(request_info).raw_post_data.

Случай 3. Этот последний случай имеет место с запросами, отличными от POST (например, PUT). php_default_post_reader называется напрямую. Поскольку запрос не является запросом POST, данные поглощаются sapi_read_standard_form_data. Поскольку данные не читаются, ничего не остается сделать.

3 голосов
/ 24 июня 2010

Возможно, они означают, что fseek () или rewind () недоступны.Вы пробовали одну из этих функций на открытом вводе php: //?

...