Доказ є у вихідному коді PHP.
Я розберу вас за допомогою швидкого процесу, як дізнатися подібну річ самостійно в будь-який час, коли захочете. Візьміть зі мною, буде багато С-вихідного коду, з яким ви зможете проглядатись (я пояснюю це). Якщо ви хочете розібратися на якомусь 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:
- Обчислює початкову / кінцеву довжину вхідного рядка, виділяє пам'ять
- Прогулянка по циклу, з кроком, поки ми не досягнемо кінця рядка
- Схоплює теперішнього персонажа
- Якщо символ дорівнює ASCII Char 0x20 (тобто "пробіл"), додайте
+
знак у вихідний рядок.
- Якщо це не пробіл, і він також не буквено-цифровий (
isalnum(c)
), а також не є і _
, -
або .
символом, то ми, виводимо %
знак на позицію масиву 0, робимо масив шукати до hexchars
масиву для пошуку os_toascii
масиву ( масив з Apache, який переводить char в шістнадцятковий код) для ключа c
(теперішнього символу), потім ми розрядно зміщуємо вправо на 4, присвоюємо це значення символу 1, а в позицію 2 присвоюємо той же пошук, за винятком того, як попередньо логічно і перевірити, чи є значення 15 (0xF), і повернути 1 у цьому випадку, або 0 в іншому випадку. Зрештою, ви закінчите щось закодоване.
- Якщо він закінчується, це не пробіл, це буквено-цифровий або один із
_-.
знаків, він виводить саме те, що є.
RAWURLENCODE:
- Виділяє пам'ять для рядка
- Ітерація над нею залежить від довжини, що надається у виклику функції (не обчислюється у функції, як у URLENCODE).
Примітка: Багато програмістів, мабуть, ніколи не бачили циклу ітерації таким чином, це дещо хакітно, а не стандартна конвенція, яка використовується для більшості циклів, зверніть увагу, вона призначає 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-адреси, за винятком того, що якщо він більший за
z
, він виключає ~
з кодування URL-адреси.
- Те саме призначення, що і ASCII RawUrlEncode
- Додавання
\0
байта до рядка перед поверненням.
Основне резюме
- Обидва використовують одну і ту ж таблицю пошуку шестигранників
- URIEncode не завершує рядок з \ 0, необроблений.
- Якщо ви працюєте в EBCDIC, я б запропонував використовувати RawUrlEncode, оскільки він керує тим,
~
що UrlEncode не робить ( це повідомляється про проблему ). Варто зазначити, що ASCII та EBCDIC 0x20 - це пробіли.
- Вони повторюються по-різному, одна може бути швидшою, може бути схильною до пам’яті або на основі струнних подвигів.
- URIEncode робить пробіл
+
, а RawUrlEncode робить пробіл у %20
пошуку через масив.
Відмова: Я не торкався С у роках, і не дивився на EBCDIC вже дуже довго. Якщо я десь помиляюся, дайте мені знати.
Пропоновані реалізації
Виходячи з усього цього, rawurlencode - це спосіб пройти більшу частину часу. Як ви бачите у відповіді Джонатана Фінгленда, дотримуйтесь її в більшості випадків. Він стосується сучасної схеми для компонентів URI, де як urlencode робить речі старої школи, де + означало "простір".
Якщо ви намагаєтеся конвертувати між старим форматом і новим форматами, переконайтеся, що ваш код не збільшиться, і перетворіть щось, що є декодованим + знаком, у простір випадковим чином подвійним кодуванням або подібними сценаріями "ой" навколо цього простір / 20% / + випуск.
Якщо ви працюєте в старшій системі зі старшим програмним забезпеченням, яке не надає перевагу новому формату, дотримуйтесь урленкоду, проте, я вважаю, що% 20 насправді буде сумісним назад, як за старим стандартом% 20 працював, просто не було кращий. Дайте йому постріл, якщо ви готові пограти, дайте нам знати, як це вийшло для вас.
В основному, ви повинні дотримуватися сировини, якщо ваша система EBCDIC насправді не ненавидить вас. Більшість програмістів ніколи не зіткнуться з EBCDIC в будь-якій системі, зробленій після 2000 року, можливо, навіть у 1990 році (це підштовхує, але все ж, на мою думку).