Ответ 1
Небольшая проверка исходного кода дает ответы.
Во-первых, да, вы ограничены одним чтением за дескриптор, потому что базовый поток не реализует обработчик 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;
}
Итак, у нас есть три возможности:
- Данные тела запроса уже прочитаны и сохранены в
SG(request_info).raw_post_data
. В этом случае, поскольку данные хранятся, мы можем открывать и читать несколько дескрипторов дляphp://input
. - Данные тела запроса были прочитаны, но его содержимое не хранилось нигде.
php://input
не может дать нам ничего. - Данные запроса еще не прочитаны. Это означает, что мы можем открыть
php://input
и прочитать его только один раз.
ПРИМЕЧАНИЕ. Ниже следует поведение по умолчанию. Различные SAPI или дополнительные расширения могут изменить это поведение.
В случае запросов POST PHP определяет другое ПОСТ-считывающее устройство и обработчик POST в зависимости от типа содержимого.
Случай 1. Это происходит, когда у нас есть запрос POST:
- С типом содержимого
application/x-www-form-encoded
.sapi_activate
обнаруживает запрос POST с типом контента и вызываетsapi_read_post_data
. Это определяет тип содержимого и определяет пару POST-считыватель/обработчик. Считыватель POSTsapi_read_standard_form_data
, который немедленно вызывается и просто копирует тело запроса наSG(request_info).post_data
. Затем вызывается читатель по умолчаниюphp_default_post_reader
, который заполняет$HTTP_RAW_POST_DATA
, если установлен параметр inialways_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
. Поскольку данные не считываются, нечего делать.