Доказательство находится в исходном коде 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 года (это, на мой взгляд, все еще актуально).