Почему может php://вход быть считанным несколько раз несмотря на документацию, говорящую иначе?

Документация PHP указывает это php://input может только быть считан однажды.

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

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


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

$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 может только быть считан однажды, когда-либо, для ПОМЕЩЕННЫХ запросов. Вышеупомянутый пример использовал запрос POST.

18
задан deceze 25 June 2010 в 04:06
поделиться

2 ответа

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

Во-первых, да, вы ограничены одним чтением на дескриптор, потому что базовый поток не реализует обработчик 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:

  • With content-type 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 , действует как смешанный считыватель / обработчик . На этапе sapi_activate никакие данные не читаются.Функция sapi_handle_post в конечном итоге вызывается на более позднем этапе, который, в свою очередь, вызывает обработчик POST. rfc1867_post_handler считывает данные запроса, заполняет POST и ФАЙЛЫ , но ничего не оставляет в SG (request_info) .raw_post_data .

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

23
ответ дан 30 November 2019 в 08:26
поделиться

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

3
ответ дан 30 November 2019 в 08:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: