urlencode против rawurlencode?

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

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

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 look-up чтобы получить некоторые значения, массив описывается следующим образом:

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

RAWURLENCODE:

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

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

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

отличия:

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

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

различия в EBCDIC:

URLENCODE:

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

RAWURLENCODE:

  • та же настройка итерации, что и в ASCII
  • такая же проверка, как описано в EBCDIC версии URL-кодирования, за исключением того, что если он больше, чем 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' while 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

что произойдет, если вы кодируете компоненты path и query string наоборот? Для следующего примера:

http://example.com/category/latest+songs/search?q=lady%20gaga
  • веб-сервер будет искать в директории latest+songs вместо latest songs
  • параметр строки запроса q содержит lady gaga

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

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

причина разницы в том, что + зарезервирован и действителен (unencoded) в 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, не так ли?

кафе!


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 более совместим в целом