86

$_GET を介して名前にPHP 変数を渡すと、PHP.はそれらを_文字に自動置換します。例えば:

<?php
echo "url is ".$_SERVER['REQUEST_URI']."<p>";
echo "x.y is ".$_GET['x.y'].".<p>";
echo "x_y is ".$_GET['x_y'].".<p>";

... 以下を出力します。

url is /SpShipTool/php/testGetUrl.php?x.y=a.b
x.y is .
x_y is a.b.

... 私の質問はこれです:これを止める方法はありますか? 私の人生では、これに値するために私が何をしたかを理解することはできません

私が実行している PHP のバージョンは 5.2.4-2ubuntu5.3 です。

4

13 に答える 13

77

PHP.netがなぜそれを行うのかについての説明は次のとおりです。

着信変数名のドット

通常、PHPは、変数がスクリプトに渡されるときに変数の名前を変更しません。ただし、ドット(ピリオド、ピリオド)はPHP変数名の有効な文字ではないことに注意してください。理由のために、それを見てください:

<?php
$varname.ext;  /* invalid variable name */
?>

ここで、パーサーが認識するのは、$ varnameという名前の変数、文字列連結演算子、裸の文字列(つまり、既知のキーや予約語と一致しない引用符で囲まれていない文字列)'ext'です。明らかに、これは意図した結果をもたらしません。

このため、PHPは入力変数名のドットを自動的にアンダースコアに置き換えることに注意することが重要です。

これはhttp://ca.php.net/variables.externalからのものです。

また、このコメントによると、これらの他の文字はアンダースコアに変換されます。

PHPが_(アンダースコア)に変換するフィールド名文字の完全なリストは次のとおりです(ドットだけではありません)。

  • chr(32)()(スペース)
  • chr(46)(。)(ドット)
  • chr(91)([)(角括弧を開く)
  • chr(128)-chr(159)(各種)

だから、あなたはそれに固執しているように見えるので、dawnerdの提案を使用してスクリプト内のアンダースコアをドットに戻す必要があります(私はstr_replaceを使用します)。

于 2008-09-16T02:01:36.047 に答える
62

長い間回答されてきた質問ですが、実際にはより良い回答 (または回避策) があります。PHP では生の入力ストリームを使用できるため、次のようなことができます。

$query_string = file_get_contents('php://input');

これにより、クエリ文字列形式の $_POST 配列が提供されます。ピリオドは本来あるべき姿です。

その後、必要に応じて解析できます(POSterのコメントに従って

<?php
// Function to fix up PHP's messing up input containing dots, etc.
// `$source` can be either 'POST' or 'GET'
function getRealInput($source) {
    $pairs = explode("&", $source == 'POST' ? file_get_contents("php://input") : $_SERVER['QUERY_STRING']);
    $vars = array();
    foreach ($pairs as $pair) {
        $nv = explode("=", $pair);
        $name = urldecode($nv[0]);
        $value = urldecode($nv[1]);
        $vars[$name] = $value;
    }
    return $vars;
}

// Wrapper functions specifically for GET and POST:
function getRealGET() { return getRealInput('GET'); }
function getRealPOST() { return getRealInput('POST'); }
?>

'.' と '.' の両方を含む OpenID パラメーターに非常に役立ちます。そして「_」にはそれぞれ意味があります!

于 2009-12-21T12:47:21.840 に答える
18

標準に準拠し、深い配列(例: )で動作するソリューションが必要です?param[2][5]=10か?

この問題の考えられるすべての原因を修正するには、PHP コードの一番上に適用します。

$_GET    = fix( $_SERVER['QUERY_STRING'] );
$_POST   = fix( file_get_contents('php://input') );
$_COOKIE = fix( $_SERVER['HTTP_COOKIE'] );

この関数の動作は、私が 2013 年の夏休みに思いついた素晴らしいアイデアです。単純な正規表現に惑わされないでください。すべてのクエリ名を取得し、それらをエンコードし (ドットが保持されるように)、通常の正規表現を使用するだけです。parse_str()関数。

function fix($source) {
    $source = preg_replace_callback(
        '/(^|(?<=&))[^=[&]+/',
        function($key) { return bin2hex(urldecode($key[0])); },
        $source
    );

    parse_str($source, $post);
    
    $result = array();
    foreach ($post as $key => $val) {
        $result[hex2bin($key)] = $val;
    }
    return $result;
}

ps: プロジェクトでこのソリューションを使用する場合は、関数に を付けてください@author Rok Kralj

于 2013-08-13T12:59:30.670 に答える
7

これは、ピリオドが変数名の無効な文字であるために発生します。その理由はPHPの実装に非常に深く依存しているため、簡単な修正は(まだ)ありません。

それまでの間、次の方法でこの問題を回避できます。

  1. php://inputPOSTデータまたは$_SERVER['QUERY_STRING']GETデータのいずれかを介して生のクエリデータにアクセスする
  2. 変換関数を使用します。

以下の変換関数(PHP> = 5.4)は、各キーと値のペアの名前を16進表現にエンコードしてから、通常のparse_str();を実行します。完了すると、16進数の名前が元の形式に戻ります。

function parse_qs($data)
{
    $data = preg_replace_callback('/(?:^|(?<=&))[^=[]+/', function($match) {
        return bin2hex(urldecode($match[0]));
    }, $data);

    parse_str($data, $values);

    return array_combine(array_map('hex2bin', array_keys($values)), $values);
}

// work with the raw query string
$data = parse_qs($_SERVER['QUERY_STRING']);

または:

// handle posted data (this only works with application/x-www-form-urlencoded)
$data = parse_qs(file_get_contents('php://input'));
于 2013-01-21T04:52:26.717 に答える
5

このアプローチは、Rok Kralj の変更されたバージョンですが、効率を改善し (不要なコールバック、影響を受けないキーのエンコードとデコードを回避する)、配列キーを正しく処理するために、いくつかの調整が行われています。

テスト付きの要旨が利用可能であり、フィードバックや提案はここまたはそこに歓迎されます。

public function fix(&$target, $source, $keep = false) {                        
    if (!$source) {                                                            
        return;                                                                
    }                                                                          
    $keys = array();                                                           

    $source = preg_replace_callback(                                           
        '/                                                                     
        # Match at start of string or &                                        
        (?:^|(?<=&))                                                           
        # Exclude cases where the period is in brackets, e.g. foo[bar.blarg]
        [^=&\[]*                                                               
        # Affected cases: periods and spaces                                   
        (?:\.|%20)                                                             
        # Keep matching until assignment, next variable, end of string or   
        # start of an array                                                    
        [^=&\[]*                                                               
        /x',                                                                   
        function ($key) use (&$keys) {                                         
            $keys[] = $key = base64_encode(urldecode($key[0]));                
            return urlencode($key);                                            
        },                                                                     
    $source                                                                    
    );                                                                         

    if (!$keep) {                                                              
        $target = array();                                                     
    }                                                                          

    parse_str($source, $data);                                                 
    foreach ($data as $key => $val) {                                          
        // Only unprocess encoded keys                                      
        if (!in_array($key, $keys)) {                                          
            $target[$key] = $val;                                              
            continue;                                                          
        }                                                                      

        $key = base64_decode($key);                                            
        $target[$key] = $val;                                                  

        if ($keep) {                                                           
            // Keep a copy in the underscore key version                       
            $key = preg_replace('/(\.| )/', '_', $key);                        
            $target[$key] = $val;                                              
        }                                                                      
    }                                                                          
}                                                                              
于 2013-08-10T15:31:14.197 に答える
4

これが発生する理由は、PHPの古いregister_globals機能が原因です。。文字は変数名の有効な文字ではないため、互換性を確保するために、PHPはそれをアンダースコアに変換します。

つまり、URL変数でピリオドを実行することはお勧めできません。

于 2008-09-16T01:56:15.687 に答える
3

文字通りPHPに「。」の置換を停止させる方法を 探している場合 $_GET または $_POST 配列内の文字を変更する場合、そのような方法の 1 つは、PHP のソースを変更することです (この場合は比較的簡単です)。

警告: PHP C ソースの変更は高度なオプションです。

同じ変更を示唆するこのPHP バグ レポートも参照してください。

探索するには、次のことが必要です。

  • PHP の C ソースコードをダウンロードする
  • .交換チェックを無効にする
  • ./configure、カスタマイズした PHP のビルドを作成してデプロイする

ソースの変更自体は簡単で、次の 1 行の半分だけを更新するだけmain/php_variables.cです。

....
/* ensure that we don't have spaces or dots in the variable name (not binary safe) */
for (p = var; *p; p++) {
    if (*p == ' ' /*|| *p == '.'*/) {
        *p='_';
....

注:オリジナルと比較し|| *p == '.' てコメントアウトされています


出力例:

の QUERY_STRING が与えられたa.a[]=bb&a.a[]=BB&c%20c=dd場合、実行すると次の<?php print_r($_GET);ようになります。

配列
(
    [aa] => 配列
        (
            [0] => bb
            [1] =>BB
        )

    [c_c] => dd
)

ノート:

  • このパッチは元の質問のみに対処します (スペースではなくドットの置換を停止します)。
  • このパッチで実行すると、スクリプトレベルのソリューションよりも高速になりますが、これらの純粋な .php の回答は依然として一般的に好まれます (PHP 自体の変更を避けるため)。
  • 理論的には、ここではポリフィル アプローチが可能であり、アプローチを組み合わせることができます。C レベルの変更をテストしparse_str()、(利用できない場合は) より遅いメソッドにフォールバックします。
于 2013-08-15T00:28:57.950 に答える
2

この問題に対する私の解決策は迅速かつ汚いものでしたが、それでも気に入っています。フォームでチェックされたファイル名のリストを投稿したかっただけです。以前base64_encodeはマークアップ内のファイル名をエンコードしてbase64_decodeから、それらを使用する前にデコードしました。

于 2011-02-07T22:35:54.737 に答える
2

Rokのソリューションを見た後、以下の私の回答、上記のcrb、およびRokのソリューションの制限に対処するバージョンを考え出しました。私の改良版をご覧ください。


上記の@crbの回答は良いスタートですが、いくつかの問題があります。

  • すべてを再処理しますが、これはやり過ぎです。「。」を含むフィールドのみ。名前で再処理する必要があります。
  • たとえば、"foo.bar[]" のようなキーの場合、ネイティブ PHP 処理と同じ方法で配列を処理できません。

以下の解決策は、現在これらの問題の両方に対処しています (最初に投稿されてから更新されていることに注意してください)。これは、私のテストでは上記の回答よりも約 50% 高速ですが、データが同じキー (または同じように抽出されるキー、たとえば foo.bar と foo_bar の両方が foo_bar として抽出される) を持つ状況を処理しません。

<?php

public function fix2(&$target, $source, $keep = false) {                       
    if (!$source) {                                                            
        return;                                                                
    }                                                                          
    preg_match_all(                                                            
        '/                                                                     
        # Match at start of string or &                                        
        (?:^|(?<=&))                                                           
        # Exclude cases where the period is in brackets, e.g. foo[bar.blarg]
        [^=&\[]*                                                               
        # Affected cases: periods and spaces                                   
        (?:\.|%20)                                                             
        # Keep matching until assignment, next variable, end of string or   
        # start of an array                                                    
        [^=&\[]*                                                               
        /x',                                                                   
        $source,                                                               
        $matches                                                               
    );                                                                         

    foreach (current($matches) as $key) {                                      
        $key    = urldecode($key);                                             
        $badKey = preg_replace('/(\.| )/', '_', $key);                         

        if (isset($target[$badKey])) {                                         
            // Duplicate values may have already unset this                    
            $target[$key] = $target[$badKey];                                  

            if (!$keep) {                                                      
                unset($target[$badKey]);                                       
            }                                                                  
        }                                                                      
    }                                                                          
}                                                                              
于 2013-08-03T01:54:05.590 に答える
0

$_POSTcrb を使用して配列全体を再作成したかったのですが、クライアントとサーバーの両方で正しくエンコードおよびデコードしていることを確認する必要があることに注意してください。文字が真に無効であり、真に有効である場合を理解することが重要です。さらに、例外なくデータベースコマンドでクライアント データを使用する前に、クライアント データを常にエスケープする必要あります。

<?php
unset($_POST);
$_POST = array();
$p0 = explode('&',file_get_contents('php://input'));
foreach ($p0 as $key => $value)
{
 $p1 = explode('=',$value);
 $_POST[$p1[0]] = $p1[1];
 //OR...
 //$_POST[urldecode($p1[0])] = urldecode($p1[1]);
}
print_r($_POST);
?>

これは個々のケースにのみ使用することをお勧めします。これをプライマリヘッダーファイルの先頭に配置することのマイナス点についてはわかりません。

于 2014-10-05T20:26:30.073 に答える
0

私の現在の解決策(前のトピックの返信に基づく):

function parseQueryString($data)
{
    $data = rawurldecode($data);   
    $pattern = '/(?:^|(?<=&))[^=&\[]*[^=&\[]*/';       
    $data = preg_replace_callback($pattern, function ($match){
        return bin2hex(urldecode($match[0]));
    }, $data);
    parse_str($data, $values);

    return array_combine(array_map('hex2bin', array_keys($values)), $values);
}

$_GET = parseQueryString($_SERVER['QUERY_STRING']);
于 2015-11-16T12:36:11.030 に答える