urlencode vs rawurlencode?

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

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

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

В компоненте запроса символы ";", "/", "?", ":", "@",
"&", "=", "+", ",", and "$"
";", "/", "?", ":", "@",
"&", "=", "+", ",", and "$"
";", "/", "?", ":", "@",
"&", "=", "+", ",", 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] = '\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) ), а также нет и _ , - , или . character, то мы hexchars знак % в позицию 0 массива, массивы смотрят на массив hexchars для поиска массива os_toascii (массив из Apache, который переводит char в шестнадцатеричный код) для ключа c (текущий символ ), мы затем побитно сдвигаем вправо на 4, присваиваем это значение символу 1, а в позицию 2 мы назначаем один и тот же поиск, за исключением того, что мы создаем логический код и видим, имеет ли значение 15 (0xF) и возвращает 1 в этот случай, иначе 0. В конце вы получите что-то закодированное.
  • Если это заканчивается, это не пробел, это буквенно-цифровой или один из _-. символов, он выводит именно то, что есть.

RAWURLENCODE:

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

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

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

Отличия:

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

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

Различия в EBCDIC:

UrlEncode:

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

RAWURLENCODE:

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

Краткий обзор

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

 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 (), тогда decodeURI("test+1") даст вам "test+1" а decodeURI("test%201") даст вам "test 1" как результат.

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

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

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

  • %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

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

urlencode () :

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