Если я хочу создать URL-адрес с помощью переменной, у меня есть два варианта кодирования строки. urlencode()
и 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 против 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).
Доказательство находится в исходном коде 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, это фактически 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.
UrlEncode:
+
в строку вывода.isalnum(c)
), а также нет и _
, -
или .
символ, тогда мы выводим знак %
на array position 0, массив ищет массив hexchars
для поиска массива os_toascii
(массив из Apache, который переводит Char в шестнадцатеричный код) для ключа c
(текущий символ), тогда мы побитно сдвигаем вправо на 4, присваиваем это значение символу 1, а в позицию 2 мы назначаем тот же поиск, за исключением того, что мы формируем логический и посмотреть, будет ли значение 15 (0xF), и вернуть 1 в этом случае, или 0 в противном случае. В конце вы получите что-то закодированное._-.
, он выводит именно то, что он есть.RAWURLENCODE:
Примечание.. Многие программисты, вероятно, никогда не видели, чтобы цикл for повторялся таким образом, он несколько хакерский, а не стандартное соглашение, используемое с большинством for-loops, обратите внимание, оно присваивает x
и y
, проверяет выход на len
, достигая 0, и увеличивает как x
, так и y
. Я знаю, это не то, что вы ожидаете, но это действительный код.
str
._-.
, и если это не так, мы выполняем почти то же задание, что и в URLENCODE, где он преформирует поиск, однако мы увеличиваем по-разному, используя y++
, а не to[1]
, это потому, что строки строятся по-разному, но в конце концов достигают одной и той же цели.\0
.Отличия:
\0
для строки, RawUrlEncode делает (это может быть спорная точка)Они в основном итерации по-разному, присваивают знак + в случае ASCII 20.
UrlEncode:
0
, за исключением .
или -
, ИЛИ меньше A
, но больше чем Char 9
, ИЛИ больше 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
Разница заключается в возвращаемых значениях, т.е.
Возвращает строку, в которой все не-буквенно-цифровые символы, кроме -_. были заменены процентом (%) затем следуют две шестнадцатеричные цифры и пробелы, закодированные как знаки плюс (+). Это кодируется так же, как опубликованные данные из формы WWW закодировано, то же самое, что и в применение/х-WWW-форм-urlencoded тип носителя. Это отличается от " RFC 1738 (см. 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">
Единственное различие заключается в том, как обрабатываются пробелы:
urlencode - на основе устаревшей реализации преобразует пробелы в +
rawurlencode - на основе RFC 1738 переводит пробелы в %20
Причина разницы заключается в том, что + зарезервирован и действителен (некодирован) в URL-адресах.
Мне бы хотелось увидеть некоторые причины для выбора одного из них... Я хочу иметь возможность просто выбрать один и использовать его навсегда с наименьшим шумом.
Достаточно честный, у меня есть простая стратегия, которую я придерживаюсь при принятии этих решений, которые я поделюсь с вами в надежде, что это может помочь.
Я думаю, что это была спецификация 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'[email protected]+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!
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
vs. +
Самая большая причина, по которой я видел использование rawurlencode()
в большинстве случаев, состоит в том, что urlencode
кодирует текстовые пространства как +
(плюс знаки), где rawurlencode
кодирует их как общепринятые %20
:
echo urlencode("red shirt");
// red+shirt
echo rawurlencode("red shirt");
// red%20shirt
Я специально видел определенные конечные точки API, которые принимают закодированные текстовые запросы, ожидающие увидеть %20
для пробела и, в результате, выходят из строя, если вместо этого используется знак плюса. Очевидно, что это будет отличаться между реализациями API, и ваш пробег может отличаться.
простой * rawurlencode путь - путь - это часть перед "?" - пробелы должны быть закодированы как %20 * urlencode строка запроса - Строка запроса - это часть после "?" -пространства лучше кодируются как "+" = rawurlencode более совместим вообще