PHP「php:// input」vs $ _POST


243

私は、JQueryからのAjaxリクエストとやり取りするときではphp://inputなく、メソッドを使用するように指示されました$_POST。私が理解していないのは、$_POSTまたはのグローバルな方法と比較してこれを使用する利点です$_GET


2
この投稿に出くわしてその素晴らしい答えを読む前に、PHP側でajax呼び出しを受け取るために「ハック」を使用していました。将来同じ問題が発生する他の人々のために、検索エンジンが私のコメントも読んでくれることを願っています!:)
aderchox 2018

回答:


484

その理由はphp://input、コンテンツタイプに関係なく、リクエストのHTTPヘッダーの後にすべての生データを返すためです。

PHPのスーパーグローバル$_POST、のみになっているのいずれかであるラップデータ

  • application/x-www-form-urlencoded (シンプルなフォーム投稿の標準コンテンツタイプ)または
  • multipart/form-data (主にファイルのアップロードに使用されます)

これは、これらがユーザーエージェントによってサポートされる必要がある唯一のコンテンツタイプであるためです。したがって、サーバーとPHPは伝統的に他のコンテンツタイプを受信することを期待していません(これは、受信できなかったという意味ではありません)。

したがって、古き良きHTMLを単にPOSTするとform、リクエストは次のようになります。

POST /page.php HTTP/1.1

key1=value1&key2=value2&key3=value3

ただし、Ajaxを頻繁に使用している場合、このprobabyには、タイプ(string、int、bool)および構造(配列、オブジェクト)とのより複雑なデータの交換も含まれるため、ほとんどの場合、JSONが最良の選択です。しかし、JSONペイロードを含むリクエストは次のようになります。

POST /page.php HTTP/1.1

{"key1":"value1","key2":"value2","key3":"value3"}

コンテンツは現在application/json(または少なくとも上記のいずれでもない)であるため、PHPの$_POST-wrapperは(まだ)その処理方法を認識していません。

データはまだそこにあり、ラッパーを介してアクセスすることはできません。そのため、file_get_contents('php://input')-encodedされていないmultipart/form-data限り)を使用して自分でraw形式でフェッチする必要があります。

これは、XMLデータまたはその他の非標準コンテンツタイプにアクセスする方法でもあります。


40
+1「これは、XMLデータまたはその他の非標準コンテンツタイプにアクセスする方法でもあります」
mandza

@Quasdank Androidアプリからクラウドのphp xamppサーバーにJSONを送信しています(stackoverflow.com/questions/36558261/…)が、file_get_contents( 'php:// input')を試したときに機能しませんでした。これは単にstring(0)を返します。これは以前はローカルマシンで機能していましたが、クラウドにデプロイした場合は機能しません。私を手伝ってくれますか?
The_Martian

1
PHPへのAJAXリクエストでXMLHttpRequestオブジェクトを使用しても、JSONを投稿する必要があるわけではないことに注意してください。これは余分なオーバーヘッドですが、クライアント側のJavaScriptはapplication / x-www-form-urlencoded形式に変換できます。ただし、変換はデータ型が純粋ではない場合があります。
Anthony Rutledge

認識されている2つのコンテンツタイプの制限は主に歴史的であると言う必要があります。PHP application/json$_POST配列の有効なデータソースとして認識されるのを妨げるものはありません。また、具体的にそのサポートを求めるリクエストも公開されています。
AnrDaemon


53

php://inputデータの生のバイトを与えることができます。これは、POSTされたデータがJSONエンコードされた構造である場合に役立ちます。これは、AJAX POSTリクエストの場合によく見られます。

これを行うための関数を次に示します。

  /**
   * Returns the JSON encoded POST data, if any, as an object.
   * 
   * @return Object|null
   */
  private function retrieveJsonPostData()
  {
    // get the raw POST data
    $rawData = file_get_contents("php://input");

    // this returns null if not valid json
    return json_decode($rawData);
  }

$_POSTあなたは伝統的なPOSTによって送信されたフォームから、キーと値のデータを処理しているとき、アレイは、より有用です。POSTされたデータは通常、認識されるフォーマットである場合にのみ動作しますapplication/x-www-form-urlencoded(参照http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4を詳細)。


7
true2番目のパラメータとしてに渡すとjson_decode、連想配列が返されることに注意してください。
Vahid Amiri 2017

28

投稿データの形式が正しくない場合、$ _ POSTには何も含まれません。しかし、php:// inputには不正な形式の文字列があります。

たとえば、ファイルをアップロードするための正しいポストのKey-Valueシーケンスを形成せず、変数名などを使用せずにすべてのファイルをポストデータとしてダンプする一部のAjaxアプリケーションがあります。$ _POSTは空になり、$ _ FILESも空になり、php:// inputは文字列として記述された正確なファイルを含みます。


22

まず、PHPに関する基本的な真実です。

PHPは、HTTPリクエストを処理するための純粋なREST(GET、POST、PUT、PATCH、DELETE)のようなインターフェースを明示的に提供するようには設計されていません

しかし、$_POST$_GET、および$_FILES スーパーグローバル、および機能は、filter_input_array()平均的な人の/素人のニーズのために非常に有用です。

の最大の隠された利点 $_POST(および$_GET)の最大の、入力データがPHPによって自動的にurldecodeされることです。特に標準のGETリクエスト内のクエリ文字列パラメーターの場合は、それを行う必要があることさえ考えません。

しかし、それからあなたはもっと学びます...

そうは言っても、プログラミングの知識が進歩し、JavaScriptの XmlHttpRequestオブジェクト(一部ではjQuery)場合、このスキームの制限に気付くでしょう。

$_POST HTTPでの2つのメディアタイプの使用に制限します Content-Typeヘッダー。

  1. application/x-www-form-urlencoded、および
  2. multipart/form-data

したがって、サーバー上のPHPにデータ値を送信し$_POST、それをスーパーグローバルに表示させる場合は、urlencodeを実行する必要があります。場合は、クライアント側でし、そのデータをキー/値のペアとして送信ます。初心者には不便な手順です。 (特に、URLの異なる部分が異なる形式のurlencodingを必要とするかどうかを判断しようとする場合:通常、未加工など)。

すべてのjQueryユーザーに対して、$.ajax()メソッドはJSONをURLエンコードされたキー/値のペアに変換してから、サーバーに送信します。を設定することで、この動作を上書きできますprocessData: false$ .ajax()のドキュメントを読んでくださいで、Content-Typeヘッダーで正しいメディアタイプを送信することを忘れないでください。

URLエンコーディング?一体何ですか!!! ???

通常、通常の同期(ページ全体が再描画されるとき)HTTPリクエストをHTMLフォームで実行している場合、ユーザーエージェント(Webブラウザ)がフォームデータをURLエンコードします。XmlHttpRequestオブジェクトを使用して非同期HTTPリクエストを行う場合、そのデータを$_POSTスーパーグローバルに表示するには、urlencoded文字列を作成して送信する必要があります

JavaScriptをどの程度利用していますか?:-)

JavaScriptの配列またはオブジェクトをURLエンコードされた文字列に変換すると、多くの開発者が(Form Dataなどの新しいAPIを使用しても)煩わしくなります。彼らはむしろJSONを送信できるだけであり、それはより効率的です、クライアントコードが送信するです。

(ウィンク、ウィンク)を覚えておいてください。平均的なWeb開発者は、 XmlHttpRequestオブジェクト、グローバル関数、文字列関数、配列関数、そしてあなたやIのような正規表現方法を ;-)。それらのUrlencodingは悪夢です。;-)

PHP、何ができますか?

PHPには直感的なXMLおよびJSON処理がないため、多くの人が気を失っています。あなたはそれが今ではPHPの一部になると思うでしょう(ため息)。

非常に多くのメディアタイプ(過去のMIMEタイプ)

XML、JSON、YAMLにはすべて、HTTP Content-Typeヘッダーに入れることができるメディアタイプがあります。

  • application / xml
  • applicaiton / json
  • application / yaml(IANAには正式な名称が記載されていません)

IANAで定義されているメディアタイプ(以前のMIMEタイプ)の数を確認します。

どのように多く見HTTPヘッダがあります。

php:// inputまたはbust

を使用して php://inputストリームすると、PHPが世界に強制されたことを抽象化のベビーシッター/手保持レベルを回避することができます。:-)大きな力には大きな責任が伴います。

さて、を通じてストリーミングされるデータ値を処理する前にphp://input、いくつかのことを行う必要があります。

  1. 正しいHTTPメソッドが示されているかどうかを判別します(GET、POST、PUT、PATCH、DELETEなど)。
  2. HTTP Content-Typeヘッダーが送信されたかどうかを確認します。
  3. Content-Typeのが目的のメディアタイプかどうかを確認します
  4. 送信されたデータが整形式の XML / JSON / YMALなどであるかどうかを確認します。
  5. 必要に応じて、データをPHPデータ型(配列またはオブジェクト)に変換します。
  6. これらの基本的なチェックまたは変換のいずれかが失敗した場合は、例外をスローします

文字エンコーディングはどうですか?

ああ、ハ!はい、アプリケーションに送信されるデータストリームをUTF-8でエンコードすることをお勧めしますが、それがエンコードされているかどうかはどうすればわかりますか?

2つの重大な問題。

  1. どれだけの量のデータが送信されるのかわかりませんphp://input
  2. データストリームの現在のエンコードが確実にわかりません。

最初にどれだけあるかを知らずにストリームデータを処理しようとしますか?それはひどい考えです。Content-Lengthスプーフィングされる可能性があるため、ストリーミングされた入力のサイズに関するガイダンスをHTTP ヘッダーだけに依存することはできません。

次のものが必要になります。

  1. ストリームサイズ検出アルゴリズム。
  2. アプリケーションで定義されたストリームサイズの制限(Apache / Nginx / PHPの制限が広すぎる可能性があります)。

ストリームの現在のエンコーディングを知らなくても、ストリームデータをUTF-8に変換しようとしますか?どうやって?iconvストリームフィルター(iconvストリームフィルターの例)では、次のように開始と終了のエンコーディングが必要なようです。

'convert.iconv.ISO-8859-1/UTF-8'

したがって、良心的であれば、次のものが必要です。

  1. ストリームエンコーディング検出アルゴリズム。
  2. 動的/ランタイムストリームフィルター定義アルゴリズム(アプリオリのエンコードの開始がわからないため)。

更新'convert.iconv.UTF-8/UTF-8'すべてを強制的にUTF-8にしますが、iconvライブラリが翻訳方法を認識していない可能性がある文字を考慮する必要があります。つまり、文字を翻訳できない場合に実行するアクションを定義する必要があります。 :1)ダミー文字を挿入します。2)失敗/スローと例外)。

Content-Encodingこれは、次のような圧縮のようなものを示す可能性があるため、HTTP ヘッダーだけに依存することはできません。これは、iconvに関して決定を下したいものではありません。

Content-Encoding: gzip

したがって、一般的な手順は次のようになります...

パートI:HTTPリクエスト関連

  1. 正しいHTTPメソッドが示されているかどうかを判別します(GET、POST、PUT、PATCH、DELETEなど)。
  2. HTTP Content-Typeヘッダーが送信されたかどうかを確認します。
  3. Content-Typeのが目的のメディアタイプかどうかを確認します

パートII:ストリームデータ関連

  1. 入力ストリームのサイズを決定します(オプション、ただし推奨)。
  2. 入力ストリームのエンコーディングを決定します。
  3. 必要に応じて、入力ストリームを目的の文字エンコーディング(UTF-8)に変換します。
  4. 必要に応じて、アプリケーションレベルの圧縮または暗号化を元に戻し、手順4、5、6を繰り返します。

パートIII:データ型関連

  1. 送信されたデータが整形式の XML / JSON / YMALなどであるかどうかを確認します。

(データは引き続きURLエンコードされた文字列である可能性があることに注意してください。この文字列は、解析してURLデコードする必要があります)。

  1. 必要に応じて、データをPHPデータ型(配列またはオブジェクト)に変換します。

パートIV:データ値関連

  1. 入力データをフィルタリングします。

  2. 入力データを検証します。

今見えますか?

$_POST入力上の制限のためにphp.iniの設定と共にスーパーグローバルは、素人のために簡単です。ただし、適切なエンコーディングの入力値をチェックするためにスーパーグローバル(または一般に配列)をループする必要がないため、ストリームを使用する場合、文字エンコーディングの処理ははるかに直感的で効率的です。


1
ああすごい!この回答ははるかに高い評価を持つ必要があります。投光照明を暗闇にしていただき、誠にありがとうございます。
Lox

最終的な分析では、PHPは基本のデフォルトを更新するのに適しています。ただし、HTTPリクエストメソッドを同じ名前のデータ構造($ _GET、$ _ POST)と組み合わせるのは論理的に誤りです。重要なのは、(1)目的のHTTPリクエストメソッド、および(2)そのリクエストを含むリクエストデータ(Content-Type)です。したがって、Perlの場合と同様に、言語の作成者/管理者の意見を喜んで犠牲にすることができます。
Anthony Rutledge

0

そこで、php:// inputストリームからPOSTデータを取得する関数を作成しました。

したがって、ここでの課題は、PUT、DELETE OR PATCHリクエストメソッドに切り替え、そのリクエストで送信されたポストデータを取得することでした。

私はこれを同じような挑戦をしている誰かのために多分共有しています。以下の関数は私が思いついたものであり、機能します。お役に立てば幸いです。

    /**
     * @method Post getPostData
     * @return array
     * 
     * Convert Content-Disposition to a post data
     */
    function getPostData() : array
    {
        // @var string $input
        $input = file_get_contents('php://input');

        // continue if $_POST is empty
        if (strlen($input) > 0 && count($_POST) == 0 || count($_POST) > 0) :

            $postsize = "---".sha1(strlen($input))."---";

            preg_match_all('/([-]{2,})([^\s]+)[\n|\s]{0,}/', $input, $match);

            // update input
            if (count($match) > 0) $input = preg_replace('/([-]{2,})([^\s]+)[\n|\s]{0,}/', '', $input);

            // extract the content-disposition
            preg_match_all("/(Content-Disposition: form-data; name=)+(.*)/m", $input, $matches);

            // let's get the keys
            if (count($matches) > 0 && count($matches[0]) > 0)
            {
                $keys = $matches[2];

                foreach ($keys as $index => $key) :
                    $key = trim($key);
                    $key = preg_replace('/^["]/','',$key);
                    $key = preg_replace('/["]$/','',$key);
                    $key = preg_replace('/[\s]/','',$key);
                    $keys[$index] = $key;
                endforeach;

                $input = preg_replace("/(Content-Disposition: form-data; name=)+(.*)/m", $postsize, $input);

                $input = preg_replace("/(Content-Length: )+([^\n]+)/im", '', $input);

                // now let's get key value
                $inputArr = explode($postsize, $input);

                // @var array $values
                $values = [];

                foreach ($inputArr as $index => $val) :
                    $val = preg_replace('/[\n]/','',$val);

                    if (preg_match('/[\S]/', $val)) $values[$index] = trim($val);

                endforeach;

                // now combine the key to the values
                $post = [];

                // @var array $value
                $value = [];

                // update value
                foreach ($values as $i => $val) $value[] = $val;

                // push to post
                foreach ($keys as $x => $key) $post[$key] = isset($value[$x]) ? $value[$x] : '';

                if (is_array($post)) :

                    $newPost = [];

                    foreach ($post as $key => $val) :

                        if (preg_match('/[\[]/', $key)) :

                            $k = substr($key, 0, strpos($key, '['));
                            $child = substr($key, strpos($key, '['));
                            $child = preg_replace('/[\[|\]]/','', $child);
                            $newPost[$k][$child] = $val;

                        else:

                            $newPost[$key] = $val;

                        endif;

                    endforeach;

                    $_POST = count($newPost) > 0 ? $newPost : $post;

                endif;
            }

        endif;

        // return post array
        return $_POST;
    }

-5

使い方の簡単な例

 <?php  
     if(!isset($_POST) || empty($_POST)) { 
     ?> 
        <form name="form1" method="post" action=""> 
          <input type="text" name="textfield"><br /> 
          <input type="submit" name="Submit" value="submit"> 
        </form> 
   <?php  
        } else { 
        $example = file_get_contents("php://input");
        echo $example;  }  
   ?>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.