PHP input stream php://input
When using xml-rpc, the server side obtains client data mainly through the PHP input stream input instead of the $_POST array. Therefore, here we mainly discuss the php input stream php://input
For an introduction to php://input, the PHP official manual document has a paragraph that clearly outlines it.
“php://input allows you to read raw POST data. It is a less memoryintensive alternative to $HTTP_RAW_POST_DATA and does not need any specialphp.ini directives. php://input is not available withenctype=”multipart/form-data” .
Translated, this is:
"php://input can read unprocessed POST data. Compared to $HTTP_RAW_POST_DATA, it puts less pressure on memory and does not require special php.ini settings. php://input cannot for enctype=multipart/form-data”
How should we understand this overview?!
I divided it into three parts and understood it step by step.
1) Read POST data
2) Cannot be used for multipart/form-data type
3) php://input VS $HTTP_RAW_POST_DATA
1. Read POST data
PHPers must be familiar with the built-in variable $_POST. What are the relationships and differences between $_POST and php://input? In addition, the most commonly used method for the client to interact with the server is GET in addition to POST. Since php://input serves as a PHP input stream, can it read GET data? These two questions are the main content we need to discuss in this section.
Experience tells us that summarizing from testing and observation will be a very effective method. Here, I wrote a few scripts to help us test.
@file 192.168.0.6:/phpinput_server.php print out the received data
@file 192.168.0.8:/phpinput_post.php Simulates submitting form data via POST method
@file 192.168.0.8:/phpinput_xmlrpc.php simulates making an xmlrpc request using the POST method.
@file 192.168.0.8:/phpinput_get.php Simulates the number of form submissions using the GET method
phpinput_server.php
1.
2. //@file phpinput_server.php
3. $raw_post_data = file_get_contents('php://input', 'r');
4. echo "-------$_POST------------------n";
5. echo var_dump($_POST) . "n";
6. echo "-------php://input-------------n";
7. echo $raw_post_data . "n";
8. ?
phpinput_post.php:
1.
2. //@file phpinput_post.php
3. $http_entity_body = 'n=' .urldecode('perfgeeks') . '&p=' . urldecode('7788');
4. $http_entity_type ='application/x-www-form-urlencoded';
5. $http_entity_length =strlen($http_entity_body);
6. $host = '192.168.0.6';
7. $port = 80;
8. $path = '/phpinput_server.php';
9. $fp = fsockopen($host, $port, $error_no,$error_desc, 30);
10. if ($fp) {
11. fputs($fp, "POST {$path} HTTP/1.1rn");
12. fputs($fp, "Host: {$host}rn");
13.
14. fputs($fp, "Content-Type: {$http_entity_type}rn");
15. fputs($fp, "Content-Length: {$http_entity_length}rn");
16. fputs($fp, "Connection: closernrn");
17. fputs($fp, $http_entity_body . "rnrn");
18.
19. while (!feof($fp)) {
20. $d .= fgets($fp, 4096);
21. }
22. fclose($fp);
23. echo $d;
24. }
25. ?>
We can capture http request packets by using the tool ngrep (because what we need to detect is php://input, we only capture http Request packets here). Let’s execute the test script phpinput_post.php
@php/phpinput_post.php
HTTP/1.1 200 OK
Date: Thu, 08 Apr 2010 03:23:36 GMT
Server: Apache/2.2.3 (CentOS)
X-Powered-By: PHP/5.1.6
Content-Length: 160
Connection: close
Content-Type: text/html; charset=UTF-8
-------$_POST------------------
array(2) {
["n"]=> string(9) "perfgeeks"
["p"]=> string(4) "7788"
}
-------php://input-------------
n=perfgeeks&p=7788
The http request packet captured through ngrep is as follows:
T 192.168.0.8:57846 -> 192.168.0.6:80[AP]
Host: 192.168.0.6
Content-Type: application/x-www-form-urlencoded
Content-Length: 18
Connection: close
n=perfgeeks&p=7788
Observing carefully, we can easily find
1. $_POST data, php://input data and httpd entity body data are "consistent"
2. The Content-Type in the http request is application/x-www-form-urlencoded, which means that the data in the http request body is the form data submitted using the http post method and has been processed by urlencode().
Let's take a look at the original file content of the script phpinput_xmlrpc.php, which simulates an xml-rpc request submitted by the POST method.
1.
2. //@file phpinput_xmlrpc.php
3.
4. $http_entity_body = "nn jt_userinfon";
5. $http_entity_type= 'text/html';
6. $http_entity_length =strlen($http_entity_body);
7.
8. $host = '192.168.0.6';
9. $port = 80;
10. $path = '/phpinput_server.php';
11. $fp = fsockopen($host, $port, $error_no,$error_desc, 30);
12. if ($fp) {
13. fputs($fp, "POST {$path} HTTP/1.1rn");
14. fputs($fp, "Host: {$host}rn");
15.
16. fputs($fp, "Content-Type: {$http_entity_type}rn");
17. fputs($fp, "Content-Length: {$http_entity_length}rn");
18. fputs($fp, "Connection: closernrn");
19. fputs($fp, $http_entity_body . "rnrn");
20. while (!feof($fp)) {
21. $d .= fgets($fp, 4096);
22. }
23.
24. fclose($fp);
25. echo $d;
26. }
27. ?>
Likewise, let’s execute this test script
@php /phpinput_xmlrcp.php
HTTP/1.1 200 OK
Date: Thu, 08 Apr 2010 03:47:18 GMT
Server: Apache/2.2.3 (CentOS)
X-Powered-By: PHP/5.1.6
Content-Length: 154
Connection: close
Content-Type: text/html; charset=UTF-8
-------$_POST------------------
array(0) {
}
-------php://input-------------
When executing this script, the http request packet we captured through ngrep is as follows
T 192.168.0.8:45570 -> 192.168.0.6:80[AP]
POST /phpinput_server.php HTTP/1.1
Host: 192.168.0.6..Content-Type: text/html
Content-Length: 75
Connection: close
1. .
2. /name>.
Likewise, I can easily find this:
1. Content-Type in http request is text/xml. It indicates that the body data in the http request is in xml data format.
2. What the server $_POST prints out is an empty array, which is inconsistent with the http entity body. This is different from the previous example. The Content-Type here is text/xml, not application/x-www-form-urlencoded
3. The php://input data is still consistent with the httpentity body data. That is, the php://input data and $_POST data are inconsistent.
Let's take a look at the situation of submitting form data through the GET method. Can php://input read the form data from the GET method? Here, we make a slight change to the phpinput_server.php file and change $_POST to $_GET.
1.
2. //@file phpinput_server.php
3. $raw_post_data =file_get_contents('php://input', 'r');
4. echo "-------$_GET------------------n";
5. echo var_dump($_GET) . "n";
6. echo"-------php://input-------------n";
7. echo $raw_post_data . "n";
8. ?>
1.
2. //@file phpinput_get.php
3. $query_path = 'n=' . urldecode('perfgeeks'). '&p=' . urldecode('7788');
4. $host = '192.168.0.6';
5. $port = 80;
6. $path = '/phpinput_server.php';
7. $d = '';
8. $fp = fsockopen($host, $port, $error_no,$error_desc, 30);
9. if ($fp) {
10. fputs($fp, "GET {$path}?{$query_path} HTTP/1.1rn");
11. fputs($fp, "Host: {$host}rn");
12. fputs($fp, "Connection: closernrn");
13.
14. while (!feof($fp)) {
15. $d .= fgets($fp, 4096);
16. }
17. fclose($fp);
18. echo $d;
19. }
20. ?>
Similarly, we execute the next phpinput_get.php test script, which simulates a normal GET method to submit form data.
@php /phpinput_get.php
HTTP/1.1 200 OK
Date: Thu, 08 Apr 2010 07:38:15 GMT
Server: Apache/2.2.3 (CentOS)
X-Powered-By: PHP/5.1.6
Content-Length: 141
Connection: close
Content-Type: text/html; charset=UTF-8
-------$_GET------------------
array(2) {
["n"]=>
string(9) "perfgeeks"
["p"]=>
string(4) "7788"
}
-------php://input-------------
At this time, using the ngrep tool, the corresponding http request packet captured is as follows
T 192.168.0.8:36775 -> 192.168.0.6:80[AP]
GET/phpinput_server.php?n=perfgeeks&p=7788 HTTP/1.1..
Host: 192.168.0.6..Connection: close....
Comparing the http requests submitted by the POST method, usually the entity body is empty in the requests submitted by the GET method. At the same time, Content-Type and Content-Length will not be specified. However, if you force the data http entity body and specify the correct Content-Type and Content-Length, then php://input can still read the http entity body data, but not the $_GET data. http://www.xiaojudeng.com
Based on the above detections, we can make the following summary:
1. When the Content-Type value is application/x-www-form-urlencoded, PHP will fill in the corresponding data of the http request body into the array $_POST, and the data filled in the $_POST array will be parsed by urldecode() result. (In fact, in addition to the Content-Type, there is also multipart/form-data indicating that the data is form data, which we will introduce later)
2. php://input data, as long as the Content-Type is not multipart/form-data (this condition will be introduced later). Then the php://input data is consistent with the httpentity body part of the data. The length of this partially consistent data is specified by Content-Length.
3. Only when the Content-Type is application/x-www-form-urlencoded and the submission method is the POST method, the $_POST data and the php://input data are "consistent" (with quotation marks, indicating that their formats and contents are inconsistent) consistent). Otherwise, they are inconsistent.
4. php://input cannot read $_GET data. This is because the $_GET data is written as query_path in the PATH field of the http request header (header), rather than in the body part of the http request.
This also helps us understand why the xml_rpc server reads data through file_get_contents(‘php://input’, ‘r’). Instead of reading from $_POST, it is precisely because the data specification of xml_rpc is xml and its Content-Type is text/xml.
php://input encountered multipart/form-data
When uploading files, the form is written like this
1.
2.
3.
4.
5.
Then, the meaning of enctype=multipart/form-data is to set the Content-Type in the header of the http request to multipart/form-data. Please check RFC1867 for its description. multipart/form-data also means submitting form data using the POST method. It is also accompanied by file upload, so it will be different from the application/x-www-form-urlencoded data format. It will be passed to the server in a more reasonable and efficient data format. We submit the form data and print out the response results, as follows:
-------$_POST------------------
array(1) { ["n"]=> string(9)"perfgeeks" }
-------php://input-------------
At the same time, the corresponding http request packets we captured through ngrep are as follows:
########
T 192.168.0.8:3981 -> 192.168.0.6:80[AP]
POST /phpinput_server.php HTTP/1.1..Host: 192.168.0.6..Connection: kee
p-alive..User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) A
ppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.3 Safari/533.2..Re
ferer: http://192.168.0.6/phpinput_server.php..Content-Length: 306..Ca
che-Control: max-age=0..Origin: http://192.168.0.6..Content-Type: mult
ipart/form-data; boundary=----WebKitFormBoundarybLQwkp4opIEZn1fA..Acce
pt:application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q
=0.8,image/png,*/*;q=0.5..Accept-Encoding: gzip,deflate,sdch..Accept-L
anguage: zh-CN,zh;q=0.8..Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3..Cook
ie:SESS3b0e658f87cf58240de13ab43a399df6=lju6o5bg8u04lv1ojugm2ccic6...
.
##
T 192.168.0.8:3981 -> 192.168.0.6:80[AP]
------WebKitFormBoundarybLQwkp4opIEZn1fA..Content-Disposition: form-da
ta;name="n"....perfgeeks..------WebKitFormBoundarybLQwkp4opIEZn1fA..C
content-Disposition: form-data; name="f";filename="test.txt"..Content-
Type: text/plain....i am file..multipart/form-data..------WebKitFormBo
undarybLQwkp4opIEZn1fA--..
##
Comparing the response output, the $_POST data is consistent with the request submission data, that is, $_POST = array(‘n’ => ‘perfgeeks’). This also echoes the data in the http request body, and also shows that PHP fills the corresponding data into the $_POST global variable. The output of php://input is empty and nothing is output, although the body in the http request packet is not empty. This means that when Content-Type is multipart/form-data, even if there is data in the HTTP request body, php://input is empty. At this time, PHP will not fill in the data into the php://input stream. Therefore, it is certain: php://input cannot be used to read enctype=multipart/form-data data.
Let's compare the http request packets captured through ngrep this time. We will find that the biggest difference is that Content-Type is followed by boundary to define the delimiter of the data, and boundary is randomly generated. Another big difference is that the data organization structure in the http entity body is different.
When the Content-Type is application/x-www-form-urlencoded, php://input and $_POST data are "consistent". When it is other Content-Type, php://input and $_POST Data data is inconsistent. Because only when the Content-Type is application/x-www-form-urlencoded or multipart/form-data, PHP will fill the corresponding part of the body data in the http request packet into the $_POST global variable. In other cases PHP ignores both. Except for php://input, which is empty when the data type is multipart/form-data, it may not be empty in other situations. Through this section, we have a better understanding of the differences and connections between php://input and $_POST. Therefore, confirm again that php://input cannot read enctype=multipart/form-data data. When php://input encounters it, it will always be empty, even if the httpentity body has data.
php://input VS $http_raw_post_data
I believe everyone already has a certain in-depth understanding of php://input. So what is $http_raw_post_data? $http_raw_post_data is a global variable built into PHP. It is used by PHP to fill the POST data into the variable $http_raw_post_data as it is when the Content-Type cannot be recognized. It also cannot read POST data whose Content-Type is multipart/form-data. You need to set the always_populate_raw_post_data value in php.ini to On so that PHP will always fill in the POST data into the variable $http_raw_post_data.
Change the script phpinput_server.php to verify the above content
$raw_post_data =file_get_contents('php://input', 'r');
$rtn = ($raw_post_data ==$HTTP_RAW_POST_DATA) ? 1 : 0;
echo $rtn;
?>
Execute test script
@php phpinput_post.php
@php phpinput_get.php
@php phpinput_xmlrpc.php
The resulting outputs are all the same, that is, they are all 1, indicating that php://input and $HTTP_RAW_POST_DATA are the same. As for the pressure on memory, we will not do detailed testing here. If you are interested, you can test and observe through xhprof.
With this, our section can be summarized as follows: http://www.xiaojudeng.com
1. php://input can read the value of the specified length in http entitybody. The length is specified by Content-Length, whether it is data submitted by POST or GET method. However, generally when the GET method submits data, the http request entity body part is empty.
2. The data read by php://input and $HTTP_RAW_POST_DATA are the same. They only read data whose Content-Type is not multipart/form-data.
Study Notes
1. Only when the Coentent-Type value is application/x-www-data-urlencoded and multipart/form-data, PHP will fill in the corresponding data in the http request packet into the global variable $_POST
2. When PHP does not recognize the Content-Type type, the corresponding data in the http request package will be filled in the variable $HTTP_RAW_POST_DATA
3. Only when Coentent-Type is not multipart/form-data, PHP will not fill in the corresponding data in the http request packet into php://input, otherwise it will be the same in other situations. The length of the padding, specified by Coentent-Length.
4. Only when the Content-Type is application/x-www-data-urlencoded, the php://input data is consistent with the $_POST data.
5. php://input data is always the same as $HTTP_RAW_POST_DATA, but php://input is more efficient than $HTTP_RAW_POST_DATA and does not require special settings php.ini
6. PHP will fill in the query_path part of the PATH field into the global variable $_GET. Normally, the body of an http request submitted by the GET method is empty.
Excerpted from Programming Life, guisu column