post - Why can php://input be read more than once despite the documentation saying otherwise? -
the php documentation states php://input
can read once.
in application need read twice, 1 time authentication purposes , 1 time processing content, , both functions handled different, independent modules. crazy thing is: it works.
can count on working everywhere, or fluke in version of php (5.2.10)? documentation can find 1 states shouldn't work, no version limitation mentioned.
following dennis' hunch, did test:
$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";
curling:
$ curl http://localhost:8888/tests/test.php -d "this test" test test
apparently it's limited 1 read per open handle.
a little more digging revealed indeed php://input
can read once, ever, for set requests. above illustration used post request.
a little inspection of source code yields answers.
first, yes, you're limited 1 read per handle because underlying stream not implement seek
handler:
php_stream_ops php_stream_input_ops = { php_stream_input_write, /* ... */ "input", null, /* seek */ /* ... */ };
second, read handler has 2 different behaviors depending on whether "post data" has been read , stored in 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; }
so have 3 possibilities here:
the request body info has been read , stored insg(request_info).raw_post_data
. in case, since info stored, can open , read multiple handles php://input
. the request body info has been read, contents not stored anywhere. php://input
cannot give anything. the request info hasn't been read yet. means can open php://input
, read once. note: follows default behavior. different sapis or additional extensions may alter behavior.
in case of post requests, php defines different post reader , post handler depending on content-type.
case 1. happens when have post request:
with content-typeapplication/x-www-form-encoded
. sapi_activate
detects post request content-type , calls sapi_read_post_data
. detects content-type , defines post reader/handler pair. post reader sapi_read_standard_form_data
, called , copies request body sg(request_info).post_data
. default post reader php_default_post_reader
called, fills $http_raw_post_data
if ini setting always_populate_post_data
set , copies sg(request_info).post_data
sg(request_info).raw_post_data
, clears first. phone call handler doesn't matter here , deferred until superglobals built (which may not happen, in case jit activated , superglobals not used). with unrecognized or inexistent content-type. in case, there's no defined post reader , handler. both cases end in php_default_post_reader
without info read. since post request , there's no reader/handler pair, sapi_read_standard_form_data
called. same function read handler content type application/x-www-form-encoded
, info gets swallowed sg(request_info).post_data
. differences on $http_raw_post_data
populated (no matter value of always_populate_post_data
) , there's no handler building superglobals. case 2. happens when have form request content-type "multipart/form-data". post reader null
, handler, rfc1867_post_handler
acts mixed reader/handler
. no info whatsoever read in sapi_activate
phase. function sapi_handle_post
called in later phase, which, in turn calls post handler. rfc1867_post_handler
reads request data, populates post
, files
, leaves nil in sg(request_info).raw_post_data
.
case 3. lastly case takes place requests different post (e.g. put). php_default_post_reader
straight called. because request not post request, info swallowed sapi_read_standard_form_data
. since no info read, there's not left done.
php post inputstream
No comments:
Post a Comment