urlencode vs rawurlencode?


Если я хочу создать URL-адрес с помощью переменной у меня есть два варианта для кодирования строки. urlencode() и rawurlencode().

в чем конкретно разница и что предпочтительнее?

11 349

11 ответов:

это будет зависеть от вашей цели. Если совместимость с другими системами важна, то кажется, что rawurlencode-это путь. Единственным исключением являются устаревшие системы, которые ожидают, что строка запроса будет следовать стилю кодирования формы пробелов, закодированных как + вместо %20 (в этом случае вам нужен urlencode).

rawurlencode следует за RFC 1738 до PHP 5.3.0 и RFC 3986 после этого (см. http://us2.php.net/manual/en/function.rawurlencode.php)

возвращает строку, в которой все не цифробуквенные символы, кроме -_.~ были заменены на знак процента ( % ), за которым следуют две шестнадцатеричные цифры. Это кодировка, описанная в " RFC 3986 для защиты буквенных символов от интерпретации в качестве специальных разделителей URL-адресов и для защиты URL-адресов от искажения средствами передачи с преобразованием символов (например, некоторые системы электронной почты).

примечание по RFC 3986 против 1738. rawurlencode до php 5.3 кодировал символ тильды (~) согласно RFC 1738. Однако, начиная с PHP 5.3, rawurlencode следует за RFC 3986, который не требует кодирования символов Тильды.

urlencode кодирует пробелы как знаки плюс (не как %20 как сделано в rawurlencode) (см. http://us2.php.net/manual/en/function.urlencode.php)

возвращает строку, в которой все не цифробуквенные символы, кроме -_. были заменены знаком процента (%) за которым следует два шестнадцатеричных числа, а пробелы кодируются как знак сложения ( + ). Он кодируется таким же образом, как и опубликованные данные из формы WWW, то есть так же, как и в типе носителя application/x-www-form-urlencoded. Это отличается от кодировки "RFC 3986" (см. rawurlencode()) тем, что по историческим причинам пробелы кодируются как знаки плюс ( + ).

это соответствует определению для application/x-www-form-urlencoded in RFC 1866.

Дополнительная Информация:

вы также можете посмотреть обсуждение на http://bytes.com/groups/php/5624-urlencode-vs-rawurlencode.

и RFC 2396 стоит посмотреть. RFC 2396 определяет допустимый синтаксис URI. Основная часть, которая нас интересует, - это компонент запроса 3.4:

в компоненте запроса символы ";", "/", "?", ":", "@",
"&", "=", "+", ",", and "$"
зарезервированы.

Как видите,+ является зарезервированным символом в строке запроса и, следовательно, должен быть закодирован в соответствии с RFC 3986 (как в rawurlencode).

доказательство находится в исходном коде PHP.

я возьму вас через быстрый процесс, как узнать такого рода вещи в будущем в любое время вы хотите. Потерпите меня, там будет много исходного кода C, который вы можете просмотреть (я объясню это). если вы хотите освежить некоторые C, хорошим местом для начала является наш SO wiki.

загрузите источник (или используйте http://lxr.php.net/ чтобы просмотреть его в интернете), grep все файлы для имя функции, вы найдете что-то вроде этого:

PHP 5.3.6 (последний на момент написания статьи) описывает две функции в их собственном коде C в файле url.c.

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);
}

хорошо, так что же здесь изменилось?

они оба по существу вызывают две различные внутренние функции соответственно: 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] = '';
    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 байт не является L в ASCII, это на самом деле <. Я уверен, что вы видите здесь путаницу.

обе эти функции управляют EBCDIC, если веб-сервер определил его.

кроме того, они оба используют массив символов (думаю типа String) 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, увеличивается до тех пор, пока мы не достигнем конца строки
  • захватывает настоящий символ
  • если символ равен ASCII Char 0x20 (т. е. "пробел"), добавьте + войдите в выходную строку.
  • если это не пробел, и это также не буквенно-цифровой (isalnum(c)), а также не является и _,- или . символ, то мы, выводим a % войдите в позицию массива 0, сделайте массив до hexchars массив для поиска os_toascii массив (массив из Apache, что означает char в шестнадцатеричный код) для ключа c (настоящий символ), затем мы побитово сдвигаем вправо на 4, присваиваем это значение символу 1, а для позиции 2 мы назначаем тот же поиск, за исключением того, что мы предварительно формируем логическое и видим, что значение равно 15 (0xF) и возвращает 1 в этом случае, или 0 в противном случае. В конце концов, вы получите что-то закодированное.
  • если он заканчивается, это не пробел, это буквенно-цифровой или один из _-. chars, он выводит именно то, что есть.

RAWURLENCODE:

  • выделяет память для строки
  • перебирает его на основе длины, указанной в вызове функции (не вычисляется в функции, как с URLENCODE).

Примечание: многие программисты, вероятно, никогда не видели, чтобы цикл for повторялся таким образом, это несколько хакерское и не стандартное соглашение, используемое с большинством for-loops, обратите внимание, оно назначает x и y проверяет выход на len достигая 0, и шагом x и y. Я знаю, это не то, что вы ожидали, но это действительный код.

  • присваивает текущий символ соответствующему символу позиция в str.
  • он проверяет, является ли данный символ буквенно-цифровым или одним из _-. символы, и если это не так, мы делаем почти то же самое назначение, что и с URLENCODE, где он преформирует поиск, однако мы увеличиваем по-разному, используя y++, а не to[1], это потому, что строки строятся по-разному, но в конце концов достигают одной и той же цели.
  • когда цикл закончен и длина ушла, он фактически завершает строку, назначение байт.
  • он возвращает закодированную строку.

отличия:

  • UrlEncode проверяет наличие пробела, присваивает знак+, RawURLEncode-нет.
  • UrlEncode не назначает байт в строку, RawUrlEncode делает (это может быть спорным моментом)
  • они повторяются по-разному, один может быть склонен к переполнению с деформированными строками, я просто предлагаю это и я не фактически расследовано.

они в основном повторяются по-разному, один назначает знак + в случае ASCII 20.

различия в EBCDIC:

URLENCODE:

  • та же настройка итерации, что и с ASCII
  • все еще переводя символ "пробел" в A + знак. Примечание-- Я думаю, что это должно быть скомпилировано в EBCDIC или вы будете в конечном итоге с ошибкой? Может кто-то редактировать и подтверждать это?
  • он проверяет, является ли данный символ символом перед 0, за исключением того, что . или -,или меньше A но больше, чем char 9,или больше Z и меньше a а не _. или больше z (да, EBCDIC немного испорчен для работы). Если он соответствует любому из них, выполните аналогичный поиск, как найдено в версии ASCII (это просто не требует поиска в os_toascii).

RAWURLENCODE:

  • та же настройка итерации, что и с ASCII
  • такая же проверка, как описано в EBCDIC версии URL Encode, за исключением того, что если она больше z, это исключает ~ из URL кодировать.
  • то же назначение, что и ASCII RawUrlEncode
  • еще добавить байт в строку, прежде чем возвращаться.

Большой Резюме

  • оба используют одну и ту же таблицу поиска hexchars
  • URIEncode не завершает строку с \0, raw делает.
  • если вы работаете в EBCDIC, я бы предложил использовать RawUrlEncode, поскольку он управляет ~ что UrlEncode не делает (об этом сообщается). Стоит отметить, что ASCII и EBCDIC 0x20 являются пробелами.
  • они повторяются по-разному, один может быть быстрее, можно быть склонным к эксплойтам на основе памяти или строк.
  • URIEncode делает пробел в +, RawUrlEncode делает пробел в %20 через поиск в массиве.

отказ от ответственности: я не касался C годами, и я не смотрел на EBCDIC в действительно очень долгое время. Если я где-то ошибаюсь, дайте мне знать.

предлагаемые реализации

исходя из всего этого, rawurlencode путь большую часть времени. Как и ты увидеть в ответ Джонатан Fingland, и придерживаться его в большинстве случаев. Он имеет дело с современной схемой для компонентов URI, где в качестве urlencode делает все по-старому, где + означает "пространство"."

если вы пытаетесь конвертировать между старым форматом и новыми форматами, убедитесь, что ваш код не ошибается и не превращает что-то, что является декодированным знаком+, в пространство случайно двойным кодированием или аналогичными сценариями "oops" вокруг этого пространства/20%/+.

если вы работая на старой системе со старым программным обеспечением, которое не предпочитает новый формат, придерживайтесь urlencode, однако я считаю, что %20 на самом деле будет обратно совместим, так как при старом стандарте %20 работал, просто не был предпочтительным. Дайте ему шанс, если вы готовы играть вокруг, дайте нам знать, как это сработало для вас.

в принципе, вы должны придерживаться raw, если ваша система EBCDIC действительно не ненавидит вас. Большинство программистов никогда не столкнутся с EBCDIC в любой системе, созданной после года 2000, может быть, даже 1990 (это толкает, но все же вероятно, на мой взгляд).

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%20asd vs asd+asd

urlencode отличается от RFC 1738 кодированием пробелов как + вместо %20

одна практическая причина, чтобы выбрать один над другим, если вы собираетесь использовать результат в другой среде, например JavaScript.

в PHP urlencode('test 1') возвращает 'test+1' во время rawurlencode('test 1') возвращает 'test%201' как результат.

но если вам нужно "декодировать" это в JavaScript с помощью decodeURI()

Я считаю, что пробелы должны быть закодированы как:

  • %20 при использовании внутри компонента URL path
  • + при использовании внутри компонента строки запроса 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
  • веб-сервер будет искать в директории latest+songs вместо latest songs
  • параметр строки запроса q содержит lady gaga

разница заключается в возвращаемых значениях, т. е.:

urlencode ():

возвращает строку, в которой все не цифробуквенные символы, кроме -_. были заменены на проценты (%) знак, за которым следуют две шестнадцатеричные цифры и пробелы кодируются как знак сложения ( + ). Оно кодируется таким же образом, что опубликованные данные из формы WWW-это закодировано, то есть так же, как и в приложение / x-www-form-urlencoded тип носителя. Это отличается от " Кодирование RFC 1738 (см. rawurlencode()) в том, что по историческим соображениям, пробелы кодируются как знак сложения ( + ).

rawurlencode():

возвращает строку, в которой все не цифробуквенные символы, кроме -_. были заменены на проценты (%) знак, за которым следуют две шестнадцатеричные цифры. Этот является ли кодировка, описанная в " RFC 1738 для защиты символов от интерпретации как специальный URL разделители, а для защита URL-адресов от того, чтобы быть искалеченным передачей носитель с преобразованием символов (например некоторые системы электронной почты).

эти два очень похожи, но последний (rawurlencode) заменит пробелы на " % "и две шестнадцатеричные цифры, которые подходят для кодирования паролей или таких, где" + " не является, например:

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

1. В чем именно заключаются различия и

единственное различие заключается в том, как обрабатываются пробелы:

urlencode-на основе устаревшей реализации преобразует пробелы в +

rawurlencode - на основании RFC 1738 переводит пробелы в %20

причина разницы заключается в том, что + зарезервирован и действителен (не закодирован) в URL-адресах.

2. который является предпочтительным?

Я бы очень хотел, чтобы увидеть некоторые причины для выбора одного над другим ... Я хочу иметь возможность просто выбрать один и использовать его навсегда с наименьшей суетой.

справедливо, у меня есть простая стратегия, которой я следую при принятии этих решений, которые я поделюсь с вами в надежде, что это может помочь.

Я думаю, что это была спецификация HTTP / 1.1 RFC 2616 который требовал "толерантный приложения"

клиенты должны быть толерантность при разборе статусной строки и серверов толерантен при разборе строки запроса.

когда вы сталкиваетесь с такими вопросами, лучшая стратегия всегда заключается в том, чтобы потреблять как можно больше и производить то, что соответствует стандартам.

так что мой совет-использовать 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 имел именно это в виду, хотя я никогда не сталкивался с отказом от любого из двух форматов, я не могу придумать лучшую стратегию для принятия в качестве стратегии defacto, не так ли?

nJoy!

urlencode: Это отличается от "Кодировка RFC 1738 (см. rawurlencode()) в том, что для исторических соображениям, пробелы кодируются как плюс (+) знаки.

Я считаю, что urlencode предназначен для параметров запроса, тогда как rawurlencode-для сегментов пути. В основном это связано с %20 для сегментов пути vs + для параметров запроса. Смотрите этот ответ, который говорит о пробелах:когда кодировать пространство в плюс ( + ) или %20?

%20 теперь работает и в параметрах запроса, поэтому rawurlencode всегда безопаснее. Однако знак плюс, как правило, используется там, где пользовательский опыт редактирования и читабельность параметров запроса имеет значение.

обратите внимание, что это означает rawurldecode не декодирует + в пробелы (http://au2.php.net/manual/en/function.rawurldecode.php). Вот почему $_GET всегда автоматически передается через urldecode, что означает + и %20 оба декодируются в помещениях.

если вы хотите, чтобы кодирование и декодирование были согласованы между входами и выходами, и вы выбрали всегда использовать + и не %20 для параметров запроса, затем urlencode это нормально для параметров запроса (ключ и значение).

вывод:

сегменты пути-всегда используйте rawurlencode / rawurldecode

параметры запроса-для декодирования всегда используйте urldecode (выполняется автоматически), для кодирования, как rawurlencode или urlencode отлично, просто выберите один, чтобы быть последовательным, особенно при сравнении URL-адресов.

пробелы кодируются как %20 и +

самая большая причина, которую я видел, чтобы использовать rawurlencode() в большинстве случаев, потому что urlencode кодирует текстовые пространства как + (знаки плюс), где rawurlencode кодирует их как обычно-видно %20:

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

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

Я специально видел определенные конечные точки API, которые принимают закодированные текстовые запросы, ожидающие увидеть %20 для пробела и, как следствие, сбой, если вместо него используется знак плюс. Очевидно, что это будет отличаться между API реализации и ваш пробег могут отличаться.

простой * rawurlencode путь - путь-это часть перед "?" - пробелы должны быть закодированы как %20 * urlencode строка запроса - Строка запроса является частью после "?" - пробелы лучше кодируются как" +" = rawurlencode более совместим в целом