Доказательство есть в исходном коде 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)
), а также не и _
, -
или .
символ, то мы выводим %
знак в позицию массива 0, чтобы массив выглядел в hexchars
массив для поиска в os_toascii
массиве ( массив из Apache, который переводит символ в шестнадцатеричный код) для ключа 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
но не a _
. ИЛИ больше, чем z
(да, EBCDIC вроде бы запутался в работе). Если он совпадает с любым из них, выполните поиск, аналогичный найденному в версии ASCII (он просто не требует поиска в os_toascii).
RAWURLENCODE:
- Те же настройки итерации, что и в ASCII
- Такая же проверка, как описано в EBCDIC-версии URL Encode, за исключением того, что если он больше
z
, он исключает ~
из URL-кодирования.
- То же назначение, что и в ASCII RawUrlEncode
- Все еще добавляя
\0
байт к строке перед возвратом.
Grand Summary
- Оба используют одну и ту же таблицу поиска hexchars
- URIEncode не завершает строку с \ 0, raw делает.
- Если вы работаете в EBCDIC, я бы предложил использовать RawUrlEncode, так как он управляет тем
~
, чего не делает UrlEncode ( это сообщенная проблема ). Стоит отметить, что ASCII и EBCDIC 0x20 являются пробелами.
- Они повторяются по-разному, кто-то может быть быстрее, кто-то может быть подвержен эксплойтам на основе памяти или строк.
- URIEncode делает пробел в
+
, RawUrlEncode делает пробел %20
через поиск в массиве.
Отказ от ответственности: я не прикасался к C в течение многих лет, и я не смотрел на EBCDIC действительно очень долгое время. Если я где-то ошибаюсь, дайте мне знать.
Предлагаемые реализации
Исходя из всего этого, rawurlencode - это то, что нужно делать большую часть времени. Как вы видите в ответе Джонатана Фингланда, придерживайтесь его в большинстве случаев. Он имеет дело с современной схемой для компонентов URI, где, как urlencode делает вещи по-старому, где + означает «пробел».
Если вы пытаетесь конвертировать между старым форматом и новыми форматами, убедитесь, что ваш код не искажается и что-то, что является символом +, превращается в пробел путем случайного двойного кодирования или аналогичных сценариев "упс" пространство / 20% / + проблема.
Если вы работаете на более старой системе с более старым программным обеспечением, которое не предпочитает новый формат, придерживайтесь urlencode, однако, я считаю, что% 20 на самом деле будет обратно совместим, как в старом стандартном% 20 работало, просто не было предпочтительным. Дайте ему шанс, если вы готовы играть, дайте нам знать, как это сработало для вас.
По сути, вы должны придерживаться raw, если ваша система EBCDIC действительно не ненавидит вас. Большинство программистов никогда не столкнутся с EBCDIC ни в одной из систем, созданных после 2000 года, может быть, даже 1990 года (это, на мой взгляд, все еще актуально).