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