พิสูจน์อยู่ในซอร์สโค้ดของ PHP
ฉันจะนำคุณเข้าสู่กระบวนการที่รวดเร็วในการค้นหาสิ่งเหล่านี้ด้วยตัวคุณเองในอนาคตเมื่อใดก็ตามที่คุณต้องการ อดทนกับฉันจะมีซอร์สโค้ด C มากมายที่คุณสามารถอ่านได้ (ฉันอธิบายได้) หากคุณต้องการที่จะแปรงขึ้นในบางส่วน C เป็นสถานที่ที่ดีที่จะเริ่มต้นคือวิกิพีเดีย
ดาวน์โหลดซอร์ส (หรือใช้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 พยายามจัดการกับทั้งคู่ แต่โดยทั่วไปหมายถึงนี้ไบต์ไบต์ 0x4c EBCDIC ไม่ได้เป็น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-loop เพิ่มขึ้นจนกว่าเราจะถึงจุดสิ้นสุดของสตริง
- คว้าตัวละครปัจจุบัน
- หากตัวละครมีค่าเท่ากับ ASCII Char 0x20 (เช่น "ช่องว่าง") ให้เพิ่ม
+
เครื่องหมายลงในสตริงเอาต์พุต
- ถ้ามันไม่ได้เป็นช่องว่างและก็ยังไม่ได้ตัวเลข (
isalnum(c)
) และยังไม่ได้และ_
, -
หรือ.
ตัวละครแล้วเราเอาท์พุท%
สัญญาณไปยังตำแหน่งอาร์เรย์ 0, ทำดูอาร์เรย์ขึ้นไปhexchars
อาร์เรย์สำหรับการค้นหาสำหรับos_toascii
อาร์เรย์ ( อาเรย์จากอาปาเช่ที่แปลอักขระชาร์ตเป็นเลขฐานสิบหก) สำหรับคีย์ของc
(ตัวละครปัจจุบัน) จากนั้นเราเลื่อนบิตทริกซ์ไปทางขวา 4 กำหนดค่านั้นให้กับตัวละคร 1 และตำแหน่งที่ 2 เรากำหนดการค้นหาเดียวกัน ตรรกะและเพื่อดูว่าค่าเป็น 15 (0xF) และส่งกลับ 1 ในกรณีนั้นหรือ 0 เป็นอย่างอื่น ในตอนท้ายคุณจะได้สิ่งที่เข้ารหัส
- ถ้ามันไม่ใช่ช่องว่างมันก็เป็นตัวอักษรและตัวเลขหรือหนึ่งใน
_-.
ตัวอักษรมันจะแสดงผลลัพธ์ออกมาอย่างแน่นอน
RAWURLENCODE:
- จัดสรรหน่วยความจำสำหรับสตริง
- วนซ้ำตามความยาวที่ระบุในการเรียกใช้ฟังก์ชัน (ไม่คำนวณในฟังก์ชั่นเช่นเดียวกับ URLENCODE)
หมายเหตุ:โปรแกรมเมอร์จำนวนมากอาจจะไม่เคยเห็นสำหรับวงสำทับด้วยวิธีนี้จะค่อนข้าง hackish และไม่ประชุมมาตรฐานที่ใช้กับที่สุดสำหรับลูปให้ความสนใจก็กำหนดx
และy
ตรวจสอบสำหรับทางออกในlen
การเข้าถึง 0, และการเพิ่มขึ้นทั้งในและx
y
ฉันรู้ว่าไม่ใช่สิ่งที่คุณคาดหวัง แต่เป็นรหัสที่ถูกต้อง
str
กำหนดตัวละครในปัจจุบันไปยังตำแหน่งที่ตรงกันในตัวละคร
- มันตรวจสอบว่าตัวละครปัจจุบันเป็นตัวอักษรและตัวเลขหรือหนึ่งใน
_-.
ตัวอักษรและถ้ามันไม่ได้เราทำเกือบจะได้รับมอบหมายเช่นเดียวกับ URLENCODE ที่มัน preforms การค้นหา แต่เราเพิ่มขึ้นแตกต่างกันใช้y++
มากกว่าto[1]
เพราะนี่คือ สตริงจะถูกสร้างขึ้นในรูปแบบที่แตกต่างกัน แต่ไปถึงเป้าหมายเดียวกันในตอนท้าย
- เมื่อการวนซ้ำเสร็จสิ้นและความยาวหายไปจริง ๆ แล้วจะเป็นการยกเลิกสตริงการกำหนด
\0
ไบต์
- มันจะส่งกลับสตริงที่เข้ารหัส
แตกต่าง:
- UrlEncode ตรวจสอบพื้นที่กำหนดสัญญาณ + RawURLEncode ไม่ได้
- UrlEncode ไม่ได้กำหนด
\0
ไบต์ให้กับสตริง RawUrlEncode ทำ (ซึ่งอาจเป็นจุดที่สงสัย)
- พวกมันย้ำไม่ต่างกันใครมีแนวโน้มที่จะล้นด้วยสตริงที่มีรูปแบบไม่ถูกต้องฉันแค่แนะนำสิ่งนี้และฉันยังไม่ได้ตรวจสอบจริง
โดยทั่วไปแล้วจะมีการทำซ้ำต่างกันหนึ่งจะกำหนดเครื่องหมาย + ในกรณีที่ ASCII 20
ความแตกต่างใน EBCDIC:
urlencode:
- ตั้งค่าการวนซ้ำเช่นเดียวกับ ASCII
- ยังคงแปลอักขระ "ช่องว่าง" เป็นเครื่องหมาย + หมายเหตุ - ฉันคิดว่าสิ่งนี้จะต้องรวบรวมใน EBCDIC หรือคุณจะจบลงด้วยข้อผิดพลาด? ใครสามารถแก้ไขและยืนยันสิ่งนี้ได้?
- มันตรวจสอบถ้าถ่านปัจจุบันเป็นถ่านก่อนที่จะ
0
มีข้อยกเว้นของการเป็นที่.
หรือ-
, หรือน้อยกว่าA
แต่มากกว่าถ่าน9
, หรือมากกว่าZ
และน้อยกว่าแต่ไม่ได้เป็นa
หรือดีกว่า(ใช่ EBCDIC เป็นระเบียบเพื่อทำงานกับ) หากตรงกับสิ่งใด ๆ ให้ทำการค้นหาที่คล้ายกันที่พบในเวอร์ชัน ASCII (ไม่ต้องค้นหาใน os_toascii)_
z
RAWURLENCODE:
- ตั้งค่าการวนซ้ำเช่นเดียวกับ ASCII
- การตรวจสอบเช่นเดียวกับที่อธิบายไว้ในการเข้ารหัส URL ของ EBCDIC รุ่นยกเว้นว่าถ้ามากกว่า
z
นั้นจะแยก~
ออกจากการเข้ารหัส URL
- การกำหนดเช่นเดียวกับ ASCII RawUrlEncode
- ยังคงต่อท้าย
\0
ไบต์กับสตริงก่อนส่งคืน
บทสรุปที่ยิ่งใหญ่
- ทั้งคู่ใช้ตารางค้นหา hexchars เดียวกัน
- URIEncode ไม่ได้ยกเลิกสตริงด้วย \ 0, raw ทำ
- หากคุณกำลังทำงานใน EBCDIC ฉันขอแนะนำให้ใช้ RawUrlEncode เนื่องจากจัดการ
~
UrlEncode นั้นไม่ได้ ( นี่เป็นปัญหาที่รายงาน ) เป็นที่น่าสังเกตว่า ASCII และ EBCDIC 0x20 เป็นช่องว่างทั้งคู่
- พวกมันซ้ำกันอย่างใดอย่างหนึ่งอาจเร็วกว่าหนึ่งอาจมีแนวโน้มที่หน่วยความจำหรือการใช้ประโยชน์จากสตริง
- URIEncode ทำให้มีช่องว่างใน
+
RawUrlEncode ทำให้มีช่องว่าง%20
ผ่านการค้นหาอาร์เรย์
คำเตือน:ฉันไม่ได้สัมผัส C เป็นเวลาหลายปีและฉันไม่ได้ดู EBCDIC ในเวลานานมาก หากฉันผิดที่ไหนสักแห่งให้ฉันรู้
การใช้งานที่แนะนำ
rawurlencode เป็นวิธีที่ใช้เวลาส่วนใหญ่ตามทั้งหมดนี้ อย่างที่คุณเห็นในคำตอบของโจนาธานฟิงแลนด์ติดอยู่กับมันในกรณีส่วนใหญ่ มันเกี่ยวข้องกับรูปแบบที่ทันสมัยสำหรับส่วนประกอบ URI ซึ่ง urlencode ทำสิ่งต่าง ๆ ในแบบโรงเรียนเก่าโดยที่ + หมายถึง "space"
หากคุณพยายามแปลงระหว่างรูปแบบเก่าและรูปแบบใหม่ตรวจสอบให้แน่ใจว่ารหัสของคุณไม่ผิดพลาดและเปลี่ยนสิ่งที่เป็นการถอดรหัส + ลงชื่อเข้าช่องว่างด้วยการเข้ารหัสสองครั้งโดยบังเอิญหรือสถานการณ์ "oops" ที่คล้ายกันรอบ ๆ นี้ พื้นที่ / ปัญหา 20% / +
หากคุณกำลังทำงานกับระบบเก่าที่มีซอฟต์แวร์รุ่นเก่าที่ไม่ชอบรูปแบบใหม่ติดกับ urlencode แต่ฉันเชื่อว่า% 20 จะสามารถใช้งานได้แบบย้อนกลับได้จริง ๆ เพราะทำงานภายใต้มาตรฐาน% 20 เก่าแล้ว แนะนำ ให้มันยิงถ้าคุณพร้อมสำหรับการเล่นรอบแจ้งให้เราทราบว่ามันทำงานออกมาสำหรับคุณ
โดยพื้นฐานแล้วคุณควรใช้ข้อมูลดิบยกเว้นว่าระบบ EBCDIC ของคุณจะเกลียดคุณจริงๆ โปรแกรมเมอร์ส่วนใหญ่จะไม่เคยพบกับ EBCDIC ในระบบใด ๆ ที่เกิดขึ้นหลังจากปี 2000 อาจจะถึงปี 1990 (นั่นคือการผลักดัน