urlencode против rawurlencode?

337

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

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

  • 1
    Мне бы очень хотелось увидеть некоторые причины для выбора одного над другим (например, проблемы, которые могут возникнуть с одним или другим), я (и я ожидаю, что другие) хочу иметь возможность просто выбрать один и использовать его навсегда с меньше всего суеты, поэтому я начал щедрость по этому вопросу.
  • 28
    @ Чальвак: Если вы хотите выбрать только один, выберите rawurlencode . Вы редко столкнетесь с системой, которая задыхается, когда заданные пробелы кодируются как %20 , в то время как системы, которые подавляются пробелами, закодированными как + встречаются чаще.
Теги:
url-encoding
urlencode

11 ответов

300
Лучший ответ

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

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

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

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

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

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

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

  • 24
    Так что же предпочтительнее?
  • 73
    rawurlencode. идти со стандартом в этом случае. urlencode хранится только для устаревшего использования
Показать ещё 6 комментариев
204

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

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

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

RAWURLENCODE:

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

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

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

Отличия:

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

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

Различия в EBCDIC:

UrlEncode:

  • Те же настройки итераций, что и в ASCII
  • По-прежнему перевод символа "пробел" на знак+. Примечание. Я думаю, что это нужно компилировать в EBCDIC, иначе у вас будет ошибка? Может ли кто-нибудь отредактировать и подтвердить это?
  • Он проверяет, является ли текущий Char Char до 0, за исключением . или -, ИЛИ меньше A, но больше чем Char 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 через поиск массива.

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

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

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

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

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

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

  • 0
    Мне никогда не приходилось беспокоиться о двойном кодировании, ведь я должен знать, что я кодировал, так как я думаю, что я делаю кодирование. Поскольку я декодирую все, что получаю, в режиме совместимости, который знает, как обрабатывать + для пространства, я также никогда не сталкивался с проблемами, о которых вы пытаетесь предупредить здесь. Я могу понять, глядя на источник, если мы не знаем, что что-то делает, но что именно мы узнали здесь, чего мы не знали уже из простого выполнения обеих функций. Я знаю, что я предвзят, но не могу не думать, что это зашло слишком далеко. Слава на усилие, хотя! знак равно
  • 2
    +1, для этой части: «Я верю, что% 20 на самом деле будет обратно совместимым, так как по старому стандарту% 20 работал, просто не был предпочтительным»
Показать ещё 1 комментарий
29
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

26

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

  • 6
    Это, безусловно, лучший ответ, который я видел. Это предложение для использования, на примере реального мира. Кроме того, это сжато.
  • 0
    Это хороший пример, хотя для этой цели я предпочитаю json_encode и JSON.parse .
19

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

  • %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
  • 2
    «Параметр строки запроса q будет содержать lady gaga » Что еще он будет содержать в противном случае? Параметр запроса q похоже, имеет одинаковое значение, передаваемое в массив $_GET независимо от использования rawurlencode или urlencode в PHP 5.2+. Тем не менее, urlencode кодируется в формате application/x-www-form-urlencoded , который является значением по умолчанию для запросов GET, поэтому я продолжаю с вашим подходом. +1
  • 2
    Я хотел уточнить, что и + и %20 декодируются как пробел при использовании в строках запроса.
5

1. В чем именно отличия и

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

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

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

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

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

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

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

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

Клиенты ДОЛЖНЫ быть толерантными при разборе строк состояния и серверов    толерантность при анализе строки запроса.

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

Итак, я советую использовать rawurlencode для создания стандартов, совместимых с RFC 1738, и использовать urldecode для обратной совместимости и размещения всего, что вы можете встретить, чтобы потреблять.

Теперь вы можете просто взять мое слово, но докажите это нам...

php > $url = <<<'EOD'
<<< > "Which, % of Alice tasks saw $s @ earnings?"
<<< > EOD;
php > echo $url, PHP_EOL;
"Which, % of Alice 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 not right???
php > echo urldecode(rawurlencode($url)), PHP_EOL;
"Which, % of Alice tasks saw $s @ earnings?"
php > // now that more like it

Казалось бы, PHP имел в виду именно это, хотя я никогда не сталкивался с тем, кто отказывался от любого из двух форматов, я не могу думать о лучшей стратегии, принятой в качестве вашей стратегии дефакто, не так ли?

NJoy!

5

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

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%[email protected]/x.txt">
  • 2
    ОП спрашивает, как узнать, что и когда использовать. Знание того, что каждый из них делает с пробелами, не помогает ОП принять решение, если он не знает важности различных возвращаемых значений.
4

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

1

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

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

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

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

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

1

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

0

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню