В документации по 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.
Небольшая проверка исходного кода дает ответы.
Во-первых, да, вы ограничены одним чтением за дескриптор, потому что базовый поток не реализует обработчик 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
типа контента 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
почтовый читатель 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 активирован и суперглобалы не используются). php_default_post_reader
без каких-либо данных. Поскольку это запрос POST, и нет пары читателя / обработчика, sapi_read_standard_form_data
. Это та же функция, что и обработчик чтения, тип application/x-www-form-encoded
типа контента application/x-www-form-encoded
, поэтому все данные проглатываются в SG(request_info).post_data
. Единственные отличия от этого $HTTP_RAW_POST_DATA
том, что всегда заполняется $HTTP_RAW_POST_DATA
(независимо от значения always_populate_post_data
), и нет обработчика для создания суперглобалов. Случай 2. Это происходит, когда у нас есть запрос формы с типом «multipart / form-data» типа контента. rfc1867_post_handler
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
. Поскольку данные не считываются, нечего делать.
Возможно, они означают, что fseek () или перемотка назад () недоступны. Вы пробовали одну из этих функций на открывшемся входе php: //?