PHP 入力ストリーム php://input
xml-rpc を使用する場合、サーバー側は、$_POST 配列ではなく、主に PHP 入力ストリーム入力を通じてクライアント データを取得します。したがって、ここでは主に php 入力ストリーム php://input
について説明します。
php://input の概要については、PHP 公式マニュアル文書に明確に概要を説明した段落があります。
「php://input を使用すると、生の POST データを読み取ることができます。$HTTP_RAW_POST_DATA に代わるメモリ消費量が少なく、特別な php.ini ディレクティブは必要ありません。php://input は enctype=”multipart/form-data では使用できません。」 .
翻訳するとこんな感じです
「php://input は未処理の POST データを読み取ることができます。$HTTP_RAW_POST_DATA と比較して、メモリへの負担が少なく、特別な php.ini 設定は必要ありません。php://input は enctype=multipart/form-data には対応できません」
この概要をどのように理解すればよいでしょうか?!
3つのパートに分けて段階的に理解していきました。
1) POSTデータを読み込む
2) multipart/form-data 型には使用できません
3) php://input VS $HTTP_RAW_POST_DATA
1.POSTデータを読み込む
PHPer は組み込み変数 $_POST に精通している必要があります。 $_POST と php://input の関係と違いは何ですか? また、クライアントがサーバーと対話するために最も一般的に使用されるメソッドは、POST に加えて GET です。 php://input は PHP の入力ストリームとして機能するので、GET データを読み込むことができますか?これら 2 つの質問が、このセクションで説明する必要がある主な内容です。
経験上、テストと観察から要約することは非常に効果的な方法であることがわかっています。ここでは、テストに役立つスクリプトをいくつか書きました。
@file 192.168.0.6:/phpinput_server.php 受信したデータを出力します
@file 192.168.0.8:/phpinput_post.php POSTメソッドによるフォームデータの送信をシミュレートします
@file 192.168.0.8:/phpinput_xmlrpc.php は、POST メソッドを使用した xmlrpc リクエストの作成をシミュレートします。
@file 192.168.0.8:/phpinput_get.php GETメソッドを使用してフォーム送信数をシミュレートします
phpinput_server.php
1.
2. //@ファイル phpinput_server.php
3. $raw_post_data = file_get_contents('php://input', 'r');
4. 「------$_POST-------n」をエコーします。
5. エコー var_dump($_POST)
6. echo "----------php://input---------------n";
7. $raw_post_data をエコーします。
8. ?
phpinput_post.php:
1.
2. //@ファイル 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. $ポート = 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}rn");
13.
14. fputs($fp, "コンテンツタイプ: {$http_entity_type}rn");
15. fputs($fp, "コンテンツの長さ: {$http_entity_length}rn");
16. fputs($fp, "接続: より近いnrn");
17. fputs($fp, $http_entity_body . "rnrn");
18.
19. while (!feof($fp)) {
20. $d .= fgets($fp, 4096);
21. }
22. fclose($fp)
23. $d をエコーします。
24. }
25.?>
ngrep ツールを使用して http リクエスト パケットをキャプチャできます (検出する必要があるのは php://input であるため、ここでは http リクエスト パケットのみをキャプチャします)。テストスクリプトphpinput_post.phpを実行してみましょう
@php/phpinput_post.php
HTTP/1.1 200 OK
日付: 2010 年 4 月 8 日木曜日 03:23:36 GMT
サーバー: Apache/2.2.3 (CentOS)
X-Powered-By: PHP/5.1.6
コンテンツの長さ: 160
接続: 閉じる
コンテンツタイプ: text/html;
-------$_POST---------------------
配列(2) {
["n"]=> 文字列(9) "perfgeeks"
["p"]=> 文字列(4) "7788"
}
-------php://input---------------
n=perfgeeks&p=7788
ngrep を通じてキャプチャされた http リクエスト パケットは次のとおりです:
T 192.168.0.8:57846 -> 192.168.0.6:80[AP]
ホスト: 192.168.0.6
コンテンツタイプ: application/x-www-form-urlencoded
コンテンツの長さ: 18
接続: 閉じる
n=perfgeeks&p=7788
注意深く観察すると、見つけるのは難しくありません
1. $_POST データ、php://input データ、httpd エンティティ本体データは「一貫性」があります
2. http リクエストの Content-Type は application/x-www-form-urlencoded です。これは、http リクエスト本文のデータが、http post メソッドを使用して送信されたフォーム データであり、urlencode() によって処理されたことを意味します。
POST メソッドによって送信された xml-rpc リクエストをシミュレートするスクリプト phpinput_xmlrpc.php の元のファイルの内容を見てみましょう。
1.
2. //@ファイル 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. $ポート = 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}rn");
15.
16. fputs($fp, "コンテンツタイプ: {$http_entity_type}rn");
17. fputs($fp, "コンテンツの長さ: {$http_entity_length}rn");
18. fputs($fp, "接続: より近いnrn");
19. fputs($fp, $http_entity_body . "rnrn");
20. while (!feof($fp)) {
21. $d .= fgets($fp, 4096);
22. }
23.
24. fclose($fp)
25. $d をエコーします。
26. }
27.?>
同様に、このテストスクリプトを実行してみましょう
@php /phpinput_xmlrcp.php
HTTP/1.1 200 OK
日付: 2010 年 4 月 8 日木曜日 03:47:18 GMT
サーバー: Apache/2.2.3 (CentOS)
X-Powered-By: PHP/5.1.6
コンテンツの長さ: 154
接続: 閉じる
コンテンツタイプ: text/html;
-------$_POST---------------------
配列(0) {
}
-------php://input---------------
<メソッドコール>
メソッドコール>
このスクリプトを実行すると、ngrep を通じてキャプチャした http リクエスト パケットは次のようになります
T 192.168.0.8:45570 -> 192.168.0.6:80[AP]
POST /phpinput_server.php HTTP/1.1
ホスト: 192.168.0.6..Content-Type: text/html
コンテンツの長さ: 75
接続: 閉じる
1. .
同様に、これも簡単に見つけることができます:
1. http リクエストの Content-Type は text/xml です。 httpリクエストのボディデータがxmlデータ形式であることを示します。
2. サーバー $_POST が出力するのは空の配列であり、http エンティティ本体と矛盾します。これは前の例とは異なります。ここでの Content-Type は application/x-www-form-urlencoded ではなく text/xml です。
3. php://input データは httpentity 本体データと一致しています。つまり、php://input データと $_POST データが矛盾しています。
GETメソッドでフォームデータを送信する場合を見てみましょう。 php://inputはGETメソッドのフォームデータを読み込むことができますか?ここでは、phpinput_server.php ファイルにわずかな変更を加え、$_POST を $_GET に変更します。
1.
2. //@ファイル phpinput_server.php
3. $raw_post_data =file_get_contents('php://input', 'r');
4. 「------$_GET-------n」をエコーします。
5. エコー var_dump($_GET)
6. echo"----------php://input---------------n";
7. $raw_post_data をエコーします。
8.?>
1.
2. //@ファイル phpinput_get.php
3. $query_path = 'n=' . urldecode('perfgeeks') . urldecode('7788');
4. $host = '192.168.0.6';
5. $ポート = 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}rn");
12. fputs($fp, "接続: より近いnrn");
13.
14. while (!feof($fp)) {
15. $d .= fgets($fp, 4096);
16. }
17. fclose($fp)
18. $d をエコーします。
19. }
20.?>
同様に、次の phpinput_get.php テスト スクリプトを実行します。これは、フォーム データを送信するための通常の GET メソッドをシミュレートします。
@php /phpinput_get.php
HTTP/1.1 200 OK
日付: 2010 年 4 月 8 日木曜日 07:38:15 GMT
サーバー: Apache/2.2.3 (CentOS)
X-Powered-By: PHP/5.1.6
コンテンツの長さ: 141
接続: 閉じる
コンテンツタイプ: text/html;
-------$_GET------
配列(2) {
["n"]=>
string(9) "perfgeeks"
["p"]=>
文字列(4) "7788"
}
-------php://input---------------
このとき、ngrepツールを使用してキャプチャした対応するhttpリクエストパケットは以下の通りです
T 192.168.0.8:36775 -> 192.168.0.6:80[AP]
GET/phpinput_server.php?n=perfgeeks&p=7788 HTTP/1.1..
ホスト: 192.168.0.6..接続: 閉じています....
上記の検出に基づいて、次のような要約を作成できます:
1. Content-Type 値が application/x-www-form-urlencoded の場合、PHP は http リクエスト本文の対応するデータを配列 $_POST に埋め込み、$_POST 配列に埋め込まれたデータは次のように解析されます。 urldecode() の結果。 (実際にはContent-Typeの他にフォームデータであることを示すmultipart/form-dataもありますが、これについては後ほど紹介します)
2. Content-Type が multipart/form-data でない限り、php://input data (この条件は後で紹介します)。この場合、php://input データは、データの httpentity 本体部分と一致します。この部分的に一貫したデータの長さは、Content-Length によって指定されます。
3. Content-Type が application/x-www-form-urlencoded で、送信メソッドが POST メソッドである場合にのみ、$_POST データと php://input データが「一貫性」を持ちます (引用符で囲み、それを示します)。形式と内容が一貫していません)、一貫性があります)。そうでなければ、それらは矛盾します。
4. php://input は $_GET データを読み取ることができません。これは、$_GET データが http リクエストの本文部分ではなく、http リクエスト ヘッダーの PATH フィールドに query_path として書き込まれているためです。
これは、xml_rpc サーバーが file_get_contents(‘php://input’, ‘r’) を通じてデータを読み取る理由を理解するのにも役立ちます。 $_POST から読み取るのではなく、xml_rpc のデータ仕様が xml であり、その Content-Type が text/xml であるためです。
php://input が multipart/form-data に遭遇しました
ファイルをアップロードする場合、フォームはこのように書きます
1.
-------$_POST---------------------
配列(1) { ["n"]=> 文字列(9)"perfgeeks" }
-------php://input---------------
同時に、ngrep を通じてキャプチャした対応する http リクエスト パケットは次のとおりです:
######
T 192.168.0.8:3981 -> 192.168.0.6:80[AP]
POST /phpinput_server.php HTTP/1.1..ホスト: 192.168.0.6..接続: kee
p-alive..ユーザーエージェント: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) A
ppleWebKit/533.2 (Gecko のような KHTML) Chrome/5.0.342.3 Safari/533.2..Re
フェラル: 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; 境界=---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
言語: zh-CN,zh;q=0.8..Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3..Cook
例: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: フォームデータ名 = "f"; ファイル名 = "test.txt"..コンテンツ-
タイプ: text/plain....私はファイルです..multipart/form-data..-----WebKitFormBo
undarybLQwkp4opIEZn1fA--..
##
応答出力を比較すると、$_POST データは要求送信データと一致しています ($_POST = array('n' => ‘perfgeeks’))。これは http リクエスト本文のデータもエコーし、PHP が対応するデータを $_POST グローバル変数に入力することも示します。 http リクエスト パケットの本文は空ではありませんが、php://input の出力は空で何も出力されません。つまり、Content-Type が multipart/form-data の場合、HTTP リクエストボディにデータがあっても、php://input は空になります。このとき、PHP は php:// にデータを入力しません。入力ストリーム。したがって、php://input を使用して enctype=multipart/form-data データを読み取ることはできないことは確かです。
今回ngrepで取得したhttpリクエストパケットを比較してみると、最大の違いはContent-Typeの後にデータの区切りを定義するboundaryが続いており、boundaryがランダムに生成されていることです。もう 1 つの大きな違いは、http エンティティ本体のデータ構成構造が異なることです。
Content-Type が application/x-www-form-urlencoded の場合、php://input と $_POST のデータは「一致」します。それ以外の Content-Type の場合、php://input と $_POST のデータは「一致」します。 「。」は矛盾しています。 Content-Type が application/x-www-form-urlencoded または multipart/form-data の場合にのみ、PHP は http リクエスト パケット内の本文データの対応する部分を $_POST グローバル変数に埋め込むためです。 PHP は両方を無視します。データ型が multipart/form-data の場合は php://input が空であることを除き、他の状況では空にならない場合があります。このセクションを通じて、php://input と $_POST の違いと関係についてより深く理解できました。したがって、php://input が enctype=multipart/form-data データを読み取ることができないことを再度確認してください。php://input がそれに遭遇すると、httpentity 本体にデータがある場合でも、常に空になります。
php://input VS $http_raw_post_data
誰もがすでに php://input についてある程度深く理解していると思います。では、$http_raw_post_data とは何でしょうか? $http_raw_post_data は、PHP に組み込まれているグローバル変数です。これは、Content-Type が認識できない場合に、POST データを変数 $http_raw_post_data にそのまま埋めるために PHP によって使用されます。また、Content-Type が multipart/form-data である POST データを読み取ることもできません。 PHP が常に POST データを変数 $http_raw_post_data に入力できるように、php.ini の always_populate_raw_post_data 値を On に設定する必要があります。
上記の内容を確認するには、phpinput_server.php スクリプトを変更します
$raw_post_data =file_get_contents('php://input', 'r');
$rtn = ($raw_post_data ==$HTTP_RAW_POST_DATA) 1 : 0;
エコー $rtn;
?>
テストスクリプトを実行します
@phpphpinput_post.php
@php phpinput_get.php
@php phpinput_xmlrpc.php
結果の出力はすべて同じです。つまり、すべて 1 であり、php://input と $HTTP_RAW_POST_DATA が同じであることを示します。メモリへの負荷に関しては、ここでは詳細なテストは行いません。興味があれば、xhprof を通じてテストして観察することができます。
これにより、このセクションは次のように要約できます: http://www.xiaojudeng.com
1. php://input は、POST メソッドで送信されたデータでも GET メソッドで送信されたデータでも、Content-Length で指定された長さの http エンティティボディの値を読み込むことができます。ただし、通常、GET メソッドがデータを送信するとき、http リクエスト エンティティの本体部分は空です。
2. php://input と $HTTP_RAW_POST_DATA で読み込まれるデータは同じであり、Content-Type が multipart/form-data ではないデータのみを読み込みます。
勉強ノート
1. Coentent-Type 値が application/x-www-data-urlencoded および multipart/form-data の場合にのみ、PHP は http リクエスト パケット内の対応するデータをグローバル変数 $_POST
に書き込みます。
2. PHP が Content-Type タイプを認識できない場合、http リクエスト パッケージ内の対応するデータが変数 $HTTP_RAW_POST_DATA に埋められます
3. Coentent-Type が multipart/form-data でない場合に限り、PHP は http リクエスト パケット内の対応するデータを php://input に書き込みません。それ以外の場合は、他の状況でも同様です。 Coentent-Length で指定されるパディングの長さ。
4. Content-Type が application/x-www-data-urlencoded の場合のみ、php://input データは $_POST データと一致します。
5. php://input データは常に $HTTP_RAW_POST_DATA と同じですが、php://input は $HTTP_RAW_POST_DATA より効率的であり、php.ini の特別な設定は必要ありません
6. PHP は、PATH フィールドの query_path 部分をグローバル変数 $_GET に書き込みます。通常、GET メソッドによって送信される http リクエストの本文は空です。
Programming Life、guisuコラムより抜粋