web-dev-qa-db-ja.com

urlencode対rawurlencode?

変数を使用してURLを作成したい場合は、文字列をエンコードする方法が2つあります。 urlencode()rawurlencode()

その違いは、正確にはどのようなものですか。

363
Gary Willoughby

それはあなたの目的によります。他のシステムとの相互運用性が重要な場合は、rawurlencodeを使用することをお勧めします。 1つの例外は、クエリ文字列が%20ではなく+としてエンコードされたフォームエンコード形式のスペースに従うことを想定している従来のシステムです(この場合はurlencodeが必要です)。

rawurlencodeは、PHP 5.3.0以前のRFC 1738、およびその後のRFC 3986に準拠しています( http:// us2を参照)。 php.net/manual/en/function.rawurlencode.php

-_。〜を除くすべての英数字以外の文字がパーセント(%)記号とそれに続く2つの16進数で置き換えられたストリングを返します。これは、リテラル文字が特殊なURLデリミタとして解釈されるのを防ぐため、および文字変換を使用して伝送メディアによってURLが破壊されるのを防ぐための"RFC 3986で説明されているエンコーディングです。

Php 5.3より前のrawurlencodeでは、RFC 1738に従ってチルダ文字(~)がエンコードされていました。ただし、PHP 5.3以降、rawurlencodeは、チルダ文字のエンコードを必要としないRFC 3986に準拠しています。

urlencodeはスペースをプラス記号としてエンコードします(rawurlencodeのように%20としてではありません)( http://us2.php.net/manualを参照) /en/function.urlencode.php

-_以外のすべての英数字以外の文字を含むストリングを返します。パーセント(%)記号の後に2つの16進数字とスペースをプラス(+)記号としてエンコードしたものに置き換えられました。 WWWフォームからの投稿データがエンコードされるのと同じ方法でエンコードされます。つまり、application/x-www-form-urlencodeされたメディアタイプと同じ方法です。これは"RFC 3986エンコーディング(rawurlencode()を参照)とは歴史的な理由でスペースがプラス(+)記号としてエンコードされるという点で異なります。

これは RFC 1866 にあるapplication/x-www-form-urlencodeの定義に対応しています。

追加の読書:

また、 http://bytes.com/groups/php/5624-urlencode-vs-rawurlencode で議論を見たいと思うかもしれません。

また、 RFC 2396 は一見の価値があります。 RFC 2396は有効なURI構文を定義しています。私たちが興味を持っている主な部分は3.4クエリコンポーネントからです。

クエリコンポーネント内では、文字";", "/", "?", ":", "@",
"&", "=", "+", ",", and "$"
は予約されています。

ご覧のとおり、+はクエリ文字列内の予約文字なので、(rawurlencodeの場合と同様に)RFC 3986に従ってエンコードする必要があります。

308

証明はPHPのソースコードにあります。

将来、いつでもこの種のことを自分で調べる方法の簡単なプロセスを紹介します。ちょっと待ってください、あなたがざっと目を通すことができるCソースコードがたくさんあります(私はそれを説明します)。 Cをブラッシュアップしたい場合は、SO wikiから始めるのが良いでしょう

ソースをダウンロードし(または http://lxr.php.net/ を使用してオンラインで参照します)、関数名のすべてのファイルをgrepします。次のようなものが見つかります。

PHP 5.3.6(執筆時点では最新)では、ファイルurl.cのネイティブCコードで2つの関数について説明しています。

RawUrlEncode()

PHP_FUNCTION(rawurlencode)
{
    char *in_str, *out_str;
    int in_str_len, out_str_len;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &in_str,
                              &in_str_len) == FAILURE) {
        return;
    }

    out_str = php_raw_url_encode(in_str, in_str_len, &out_str_len);
    RETURN_STRINGL(out_str, out_str_len, 0);
}

UrlEncode()

PHP_FUNCTION(urlencode)
{
    char *in_str, *out_str;
    int in_str_len, out_str_len;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &in_str,
                              &in_str_len) == FAILURE) {
        return;
    }

    out_str = php_url_encode(in_str, in_str_len, &out_str_len);
    RETURN_STRINGL(out_str, out_str_len, 0);
}

さて、ここで何が違うのですか?

これらは両方とも、本質的に2つの異なる内部関数をそれぞれ呼び出しています:php_raw_url_encodeおよびphp_url_encode

これらの機能を探しに行きましょう!

Php_raw_url_encodeを見てみましょう

PHPAPI char *php_raw_url_encode(char const *s, int len, int *new_length)
{
    register int x, y;
    unsigned char *str;

    str = (unsigned char *) safe_emalloc(3, len, 1);
    for (x = 0, y = 0; len--; x++, y++) {
        str[y] = (unsigned char) s[x];
#ifndef CHARSET_EBCDIC
        if ((str[y] < '0' && str[y] != '-' && str[y] != '.') ||
            (str[y] < 'A' && str[y] > '9') ||
            (str[y] > 'Z' && str[y] < 'a' && str[y] != '_') ||
            (str[y] > 'z' && str[y] != '~')) {
            str[y++] = '%';
            str[y++] = hexchars[(unsigned char) s[x] >> 4];
            str[y] = hexchars[(unsigned char) s[x] & 15];
#else /*CHARSET_EBCDIC*/
        if (!isalnum(str[y]) && strchr("_-.~", str[y]) != NULL) {
            str[y++] = '%';
            str[y++] = hexchars[os_toascii[(unsigned char) s[x]] >> 4];
            str[y] = hexchars[os_toascii[(unsigned char) s[x]] & 15];
#endif /*CHARSET_EBCDIC*/
        }
    }
    str[y] = '\0';
    if (new_length) {
        *new_length = y;
    }
    return ((char *) str);
}

そしてもちろん、php_url_encode:

PHPAPI char *php_url_encode(char const *s, int len, int *new_length)
{
    register unsigned char c;
    unsigned char *to, *start;
    unsigned char const *from, *end;

    from = (unsigned char *)s;
    end = (unsigned char *)s + len;
    start = to = (unsigned char *) safe_emalloc(3, len, 1);

    while (from < end) {
        c = *from++;

        if (c == ' ') {
            *to++ = '+';
#ifndef CHARSET_EBCDIC
        } else if ((c < '0' && c != '-' && c != '.') ||
                   (c < 'A' && c > '9') ||
                   (c > 'Z' && c < 'a' && c != '_') ||
                   (c > 'z')) {
            to[0] = '%';
            to[1] = hexchars[c >> 4];
            to[2] = hexchars[c & 15];
            to += 3;
#else /*CHARSET_EBCDIC*/
        } else if (!isalnum(c) && strchr("_-.", c) == NULL) {
            /* Allow only alphanumeric chars and '_', '-', '.'; escape the rest */
            to[0] = '%';
            to[1] = hexchars[os_toascii[c] >> 4];
            to[2] = hexchars[os_toascii[c] & 15];
            to += 3;
#endif /*CHARSET_EBCDIC*/
        } else {
            *to++ = c;
        }
    }
    *to = 0;
    if (new_length) {
        *new_length = to - start;
    }
    return (char *) start;
}

先に進む前に、ちょっとした知識 EBCDICは別の文字セットです 、ASCIIに似ていますが、完全に競合しています。 PHPは両方に対処しようとします。しかし基本的に、これはバイトEBCDIC 0x4cバイトがASCIIのLではなく、実際には<であることを意味します。ここで混乱が生じると思います。

Webサーバーで定義されている場合、これらの機能は両方ともEBCDICを管理します。

また、両方ともcharの配列(文字列型と考えてください)hexcharsルックアップを使用して値を取得します。配列は次のように記述されます。

/* rfc1738:

   ...The characters ";",
   "/", "?", ":", "@", "=" and "&" are the characters which may be
   reserved for special meaning within a scheme...

   ...Thus, only alphanumerics, the special characters "$-_.+!*'(),", and
   reserved characters used for their reserved purposes may be used
   unencoded within a URL...

   For added safety, we only leave -_. unencoded.
 */

static unsigned char hexchars[] = "0123456789ABCDEF";

それを超えて、関数は本当に異なっているので、ASCIIとEBCDICで説明します。

ASCIIの違い:

URLENCODE:

  • 入力文字列の開始/終了の長さを計算し、メモリを割り当てます
  • While-loopを歩き、文字列の最後に達するまで増分します
  • 現在のキャラクターをつかむ
  • 文字がASCII Char 0x20(つまり、「スペース」)と等しい場合、+記号を出力文字列に追加します。
  • スペースではなく、英数字(isalnum(c))でもなく、_-、または.文字でもない場合、配列位置0に%記号を出力し、配列を検索しますhexchars(現在の文字)のキーのos_toascii配列( Apacheが変換する charから16進コードへの配列)のルックアップのためのc配列に、4ビットずつ右にシフトします。値を文字1に、位置2に同じルックアップを割り当てます。ただし、論理を実行し、値が15(0xF)であるかどうかを確認し、その場合は1を返します。最後に、エンコードされたものになります。
  • 最終的にスペースではなく、英数字または_-.文字のいずれかである場合、正確にそれが何であるかを出力します。

RAWURLENCODE:

  • 文字列にメモリを割り当てます
  • 関数呼び出しで指定された長さに基づいて反復します(URLENCODEのように関数で計算されません)。

注:多くのプログラマーは、おそらくforループがこの方法で繰り返されるのを見たことがないでしょう。これはややハック的で、ほとんどのforループで使用される標準の規則ではなく、注意を払って、xyを割り当て、 lenが0に到達すると終了し、xyの両方をインクリメントします。私は知っている、それはあなたが期待するものではありませんが、それは有効なコードです。

  • 現在の文字をstrの一致する文字位置に割り当てます。
  • 現在の文字が英数字か、_-.文字の1つかをチェックし、そうでない場合は、ルックアップを実行するURLENCODEとほぼ同じ割り当てを行いますが、y++ではなくto[1]を使用して、インクリメントします。文字列はさまざまな方法で構築されていますが、いずれにしても最終的には同じ目標に到達するからです。
  • ループが完了し、長さがなくなると、実際に文字列を終了し、\0バイトを割り当てます。
  • エンコードされた文字列を返します。

違い:

  • UrlEncodeはスペースをチェックし、+記号を割り当てますが、RawURLEncodeは割り当てません。
  • UrlEncodeは\0バイトを文字列に割り当てませんが、RawUrlEncodeは割り当てます(これは重要なポイントかもしれません)
  • それらは異なって繰り返され、不正な形式の文字列でオーバーフローする傾向があります。私は単にこれを提案しているだけでこれと私は実際には調査していません.

基本的に異なる方法で繰り返され、ASCII 20のイベントに+記号を割り当てます。

EBCDICの違い:

URLENCODE:

  • ASCIIと同じ反復セットアップ
  • 「スペース」文字を+記号に変換しています。注-これはEBCDICでコンパイルする必要があると思いますか、それともバグになりますか?誰かがこれを編集して確認できますか?
  • 現在の文字が0の前の文字かどうかをチェックします。ただし、.または-、またはAよりも小さいがCHAR 9よりも大きいである場合は例外ですまたはZより大きく、aより小さいが、_ではない。 またはzより大きい(ええ、EBCDICは動作するように少し混乱しています)。それらのいずれかに一致する場合は、ASCIIバージョンにあるのと同様の検索を実行します(os_toasciiでの検索は不要です)。

RAWURLENCODE:

  • ASCIIと同じ反復セットアップ
  • URL EncodeのEBCDICバージョンで説明されているのと同じチェック。ただし、zより大きい場合は、~がURLエンコードから除外されます。
  • ASCII RawUrlEncodeと同じ割り当て
  • 戻る前に文字列に\0バイトを追加します。

総括

  • 両方とも同じhexcharsルックアップテーブルを使用します
  • URIEncodeは、文字列を\ 0で終了しませんが、rawは終了します。
  • EBCDICで作業している場合は、UrlEncodeが管理しない~を管理するため、RawUrlEncodeを使用することをお勧めします( これは報告された問題です )。 ASCIIとEBCDIC 0x20は両方ともスペースであることに注意してください。
  • これらは異なる方法で繰り返され、高速化されたり、メモリベースまたは文字列ベースのエクスプロイトを受けやすくなります。
  • URIEncodeは+にスペースを作り、RawUrlEncodeは配列ルックアップを介して%20にスペースを作ります。

免責事項:私は何年もCに触れたことがなく、本当に長い間EBCDICを見ていない。私がどこか間違っているなら、私に知らせてください。

推奨される実装

これらすべてに基づいて、rawurlencodeはほとんどの場合に使用できます。ジョナサンフィンランドの答えをご覧ください。ほとんどの場合、それを守ってください。 urlencodeがオールドスクールのやり方で行うように、URIコンポーネントの最新のスキームを扱います。ここで、+は「スペース」を意味します。

古い形式と新しい形式の間で変換しようとしている場合は、コードが上手く行かないようにし、誤って二重エンコードするか、これに関する同様の「おっと」シナリオによって、デコードされた+記号であるものをスペースに変えないようにしてくださいスペース/ 20%/ +の問題。

新しい形式を好まない古いソフトウェアを使用して古いシステムで作業している場合は、urlencodeに固執しますが、%20は実際には下位互換性があると思います。望ましい。あなたが遊んでいるなら、それを試してみてください、それがあなたのためにどのように働いたか教えてください。

基本的に、EBCDICシステムが本当に嫌な場合を除き、rawのままにしてください。ほとんどのプログラマーは、2000年以降、おそらくは1990年以降に作成されたシステムでEBCDICに遭遇することはありません(それはプッシュですが、それでも私の意見ではそうです)。

209
Incognito
echo rawurlencode('http://www.google.com/index.html?id=asd asd');

収量

http%3A%2F%2Fwww.google.com%2Findex.html%3Fid%3Dasd%20asd

しながら

echo urlencode('http://www.google.com/index.html?id=asd asd');

収量

http%3A%2F%2Fwww.google.com%2Findex.html%3Fid%3Dasd+asd

違いはasd%20asdasd+asdです。

urlencodeは、スペースを+ではなく%20としてエンコードする点でRFC 1738と異なります

34
jitter

一方を選択する実際的な理由の1つは、JavaScriptなどの別の環境で結果を使用する場合です。

結果として、PHP urlencode('test 1')'test+1'を返し、rawurlencode('test 1')'test%201'を返します。

しかし、JavaScriptでdecodeURI() functionを使用してこれを「デコード」する必要がある場合、decodeURI("test+1")から"test+1"が返され、decodeURI("test%201")から"test 1"が返されます。

つまり、PHP内のrlencodeからプラス( "+")にエンコードされたスペース( "")は、JavaScriptではdecodeURIによって正しくデコードされません。 。

そのような場合はrawurlencode PHP関数を使うべきです。

27
Neven Boyanov

スペースは次のようにエンコードする必要があると思います。

  • URLパスコンポーネント内で使用する場合は%20
  • URLクエリ文字列コンポーネントまたはフォームデータ内で使用される場合の+17.13.4フォームコンテンツタイプ を参照)

次の例は、 rawurlencode および urlencode の正しい使い方を示しています。

echo "http://example.com"
    . "/category/" . rawurlencode("latest songs")
    . "/search?q=" . urlencode("lady gaga");

出力:

http://example.com/category/latest%20songs/search?q=lady+gaga

パスとクエリ文字列の構成要素を逆にエンコードするとどうなりますか?次の例では

http://example.com/category/latest+songs/search?q=lady%20gaga
  • Webサーバーはlatest+songsの代わりにディレクトリlatest songsを探します。
  • クエリ文字列パラメータqにはlady gagaが含まれます。
20
Salman A

1.違いは何ですか?

唯一の違いはスペースの扱い方です。

urlencode - 従来の実装に基づき、スペースを+に変換します

rawurlencode - RFC 1738 に基づいて、スペースを%20に変換します。

違いがあるのは、+がURLで予約されていて有効(エンコードされていない)だからです。

どちらが好ましいですか?

私は本当に1つを他のものよりも選ぶためのいくつかの理由を見たいと思います...私はただ1つを選びそして最小の煩わしさで永遠にそれを使用できるようにしたいです。

私はこれらの決定を下す際に従うという単純な戦略を持っています。

私はそれがを要求したHTTP/1.1仕様 RFC 2616 だと思います--- アプリケーションの許容範囲 "

クライアントはStatus-Lineの解析に寛容であり、サーバーはRequest-Lineの解析に寛容であるべきです(SHOULD)。

このような質問に直面したときの最良の戦略は、常にできるだけ多くを消費して、標準に準拠したものを作成することです。

ですから私のアドバイスはrawurlencodeを使って標準に準拠したRFC 1738でエンコードされた文字列を作成し、urldecodeを使って下位互換性を保ち、遭遇する可能性があるものすべてに対応することです。

今、あなたは私のことばをそれに代えることができますが、それを証明することができます...

php > $url = <<<'EOD'
<<< > "Which, % of Alice's tasks saw $s @ earnings?"
<<< > EOD;
php > echo $url, PHP_EOL;
"Which, % of Alice's tasks saw $s @ earnings?"
php > echo urlencode($url), PHP_EOL;
%22Which%2C+%25+of+Alice%27s+tasks+saw+%24s+%40+earnings%3F%22
php > echo rawurlencode($url), PHP_EOL;
%22Which%2C%20%25%20of%20Alice%27s%20tasks%20saw%20%24s%20%40%20earnings%3F%22
php > echo rawurldecode(urlencode($url)), PHP_EOL;
"Which,+%+of+Alice's+tasks+saw+$s+@+earnings?"
php > // oops that's not right???
php > echo urldecode(rawurlencode($url)), PHP_EOL;
"Which, % of Alice's tasks saw $s @ earnings?"
php > // now that's more like it

PHPはまさにこれを念頭に置いていたようです。2つの形式のどちらも拒否している人はいませんが、事実上の戦略として採用するより良い戦略を考えることはできませんか。

nジョイ!

5
nickl-

違いは戻り値にあります。

rlencode()

-_以外のすべての英数字以外の文字を含むストリングを返します。パーセント(%)記号の後に2つの16進数字とスペースをプラス(+)記号としてエンコードしたものに置き換えられました。 WWWフォームからの投稿データがエンコードされるのと同じ方法でエンコードされます。つまり、application/x-www-form-urlencodeされたメディアタイプと同じ方法です。これは"RFC 1738エンコーディング(rawurlencode()を参照)とは歴史的な理由でスペースがプラス(+)記号としてエンコードされるという点で異なります。

rawurlencode()

-_以外のすべての英数字以外の文字を含むストリングを返します。パーセント(%)記号とそれに続く2桁の16進数に置き換えられました。これは、リテラル文字が特殊なURLデリミタとして解釈されるのを防ぐため、および文字変換を使用して伝送メディアによってURLが破壊されるのを防ぐための"RFC 1738で説明されているエンコーディングです。

この2つは非常に似ていますが、後者(rawurlencode)はスペースを '%'と2桁の16進数に置き換えます。これは、パスワードのエンコードなどに適しています。

echo '<a href="ftp://user:', rawurlencode('foo @+%/'),
     '@ftp.example.com/x.txt">';
//Outputs <a href="ftp://user:foo%20%40%2B%25%[email protected]/x.txt">
5
karim79

rlencode :これは"RFC 1738エンコーディング(rawurlencode()を参照)とは歴史的な理由でスペースがプラス(+)記号としてエンコードされているという点で異なります。

4
Remus Rusanu

%20+としてエンコードされたスペース

私がほとんどの場合rawurlencode()を使用するのを見た最大の理由は、urlencodeがテキストスペースを+(プラス記号)としてエンコードするのに対し、rawurlencodeは一般的に見られる%20としてエンコードするからです。

echo urlencode("red shirt");
// red+shirt

echo rawurlencode("red shirt");
// red%20shirt

エンコードされたテキストクエリを受け付ける特定のAPIエンドポイントでは、スペースに%20が表示されることを期待しています。その結果、代わりにプラス記号を使用すると失敗します。明らかにこれはAPI実装の間で異なり、あなたの走行距離は変わるかもしれません。

1
Jake Wilson

Urlencodeはクエリパラメータ用であり、rawurlencodeはパスセグメント用です。これは主に、パスセグメントの%20とクエリパラメータの+が原因です。スペースについて話しているこの答えを参照してください。 スペースをプラス(+)または%20にエンコードする場合は?

しかし%20はクエリパラメータでも機能するようになったため、rawurlencodeの方が常に安全です。ただし、プラス記号は、ユーザーによる編集の経験とクエリパラメータの読みやすさが重要な場合に使用される傾向があります。

これはrawurldecode+をスペースにデコードしないことを意味することに注意してください( http://au2.php.net/manual/en/function.rawurldecode.php )。これが、$ _ GETが常にurldecodeを介して自動的に渡される理由です。つまり、+%20は両方ともスペースにデコードされます。

符号化と復号化を入力と出力の間で一貫させ、常にクエリパラメータに+ではなく%20を使用することを選択した場合、クエリパラメータ(キーと値)にはurlencodeを使用します。

結論は次のとおりです。

パスセグメント - 常にrawurlencode/rawurldecodeを使用

クエリパラメータ - デコードには常にurldecodeを使用し(自動的に行われます)、エンコードにはrawurlencodeとurlencodeのどちらでも問題ありません。特にURLを比較する場合は、一貫性があるものを選択してください。

1
CMCDragonkai

simple * rawurlencodeパス - パスは "?"の前の部分です。 - スペースは%20 *としてエンコードする必要があります。*クエリ文字列のURLエンコード - クエリ文字列は "?"の後の部分です。 "+" = rawurlencodeの方が一般的により互換性があるので、スペースはよりよくエンコードされます。

0
haysam elmasry