urlencode против rawurlencode? - PullRequest
363 голосов
/ 15 июня 2009

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

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

Ответы [ 11 ]

308 голосов
/ 15 июня 2009

Это будет зависеть от вашей цели. Если важна совместимость с другими системами, тогда кажется, что стоит использовать 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 в RFC 1866 .

Дополнительное чтение:

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

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

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

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

207 голосов
/ 09 августа 2011

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

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

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

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] = '\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 - это не L в ASCII, а на самом деле <. Я уверен, что вы видите здесь путаницу.

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

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

RAWURLENCODE:

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

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

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

Отличия:

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

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

Различия в EBCDIC:

UrlEncode:

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

RAWURLENCODE:

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

Общее резюме

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

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

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

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

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

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

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

34 голосов
/ 15 июня 2009
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 против asd+asd

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

27 голосов
/ 21 декабря 2011

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

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

Но если вам нужно «декодировать» это в JavaScript с помощью функции decodeURI () , тогда decodeURI("test+1") даст вам "test+1", а decodeURI("test%201") даст вам "test 1" в результате.

Другими словами, пробел (""), закодированный urlencode в плюс ("+") в PHP, не будет правильно декодироваться с помощью decodeURI в JavaScript.

В таких случаях следует использовать PHP-функцию rawurlencode .

20 голосов
/ 23 сентября 2012

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

  • %20 при использовании внутри компонента пути URL
  • + при использовании внутри компонента строки запроса 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
5 голосов
/ 28 ноября 2012

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

NJoy!

5 голосов
/ 15 июня 2009

Разница в возвращаемых значениях, т.е.:

UrlEncode ()

Возвращает строку, в которой все не буквенно-цифровые символы, кроме -_. были заменены на процент (%) знак, за которым следуют две шестнадцатеричные цифры и пробелы, закодированные как знаки плюс (+). Это кодируется так же, как размещенные данные из WWW формы закодирован, то же самое, что и в применение / х-WWW-форм-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">
4 голосов
/ 15 июня 2009

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

1 голос
/ 28 июля 2016

Пробелы, закодированные как %20 против +

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

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

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

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

1 голос
/ 07 февраля 2014

Я считаю, что 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.

...