Wednesday 15 September 2010

post - Why can php://input be read more than once despite the documentation saying otherwise? -



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 in sg(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-type application/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