Если я хочу создать 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
Так что ищите эти функции!
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); }
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.
UrlEncode:
+
в строку вывода. isalnum(c)
), а также нет и _
, -
, или .
character, то мы hexchars
знак %
в позицию 0 массива, массивы смотрят на массив hexchars
для поиска массива os_toascii
(массив из Apache, который переводит char в шестнадцатеричный код) для ключа c
(текущий символ ), мы затем побитно сдвигаем вправо на 4, присваиваем это значение символу 1, а в позицию 2 мы назначаем один и тот же поиск, за исключением того, что мы создаем логический код и видим, имеет ли значение 15 (0xF) и возвращает 1 в этот случай, иначе 0. В конце вы получите что-то закодированное. _-.
символов, он выводит именно то, что есть. RAWURLENCODE:
Примечание. Многие программисты, вероятно, никогда не видели, чтобы цикл цикла повторялся таким образом, он несколько хакерский, а не стандартное соглашение, используемое с большинством for-loops, обратите внимание, оно назначает x
и y
, проверяет, чтобы выход на len
достиг 0, и увеличивает x
и y
. Я знаю, это не то, что вы ожидаете, но это правильный код.
str
. _-.
chars, и если это не так, мы выполняем почти то же задание, что и в URLENCODE, где он формирует поиск, однако мы увеличиваем разные значения, используя y++
а не to[1]
, потому что строки строятся по-разному, но достичь цели в конце концов в любом случае. \0
байт. Отличия:
\0
байту, RawUrlEncode делает (это может быть спорная точка) Они в основном итерации по-разному, присваивают знак + в случае ASCII 20.
UrlEncode:
0
, за исключением того, что он является a .
или -
, ИЛИ меньше A
но больше, чем char 9
, OR больше Z
и меньше, чем a
но не _
. ИЛИ больше, чем z
(да, EBCDIC немного испорчен для работы). Если он соответствует любому из них, выполните аналогичный поиск, найденный в версии ASCII (он просто не требует поиска в os_toascii). RAWURLENCODE:
z
, она исключает ~
из кодировки URL. \0
в строку перед возвратом. ~
что UrlEncode не делает ( это проблема с сообщением ). Стоит отметить, что ASCII и EBCDIC 0x20 являются одновременно пробелами. +
, 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">
Единственное различие заключается в том, как обрабатываются пробелы:
urlencode – на основе старой реализации преобразует пробелы в +
rawurlencode – на основе RFC 1738 переводит пробелы в% 20
Причина разницы в том, что + зарезервирован и действителен (некодирован) в URL-адресах.
Мне бы очень хотелось увидеть некоторые причины для выбора одного над другим … Я хочу иметь возможность просто выбрать один и использовать его навсегда с наименьшей суетой.
Достаточно справедливо, у меня есть простая стратегия, которую я придерживаюсь при принятии этих решений, которые я поделюсь с вами в надежде, что это может помочь.
Я думаю, что это спецификация 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 более совместим в целом