Bằng chứng là trong mã nguồn của PHP.
Tôi sẽ đưa bạn qua một quá trình nhanh chóng để tự mình tìm ra loại điều này trong tương lai bất cứ lúc nào bạn muốn. Hãy kiên nhẫn với tôi, sẽ có rất nhiều mã nguồn C bạn có thể lướt qua (tôi giải thích nó). Nếu bạn muốn theo dõi một số C, một nơi tốt để bắt đầu là wiki SO của chúng tôi .
Tải xuống nguồn (hoặc sử dụng http://lxr.php.net/ để duyệt trực tuyến), grep tất cả các tệp cho tên hàm, bạn sẽ tìm thấy một cái gì đó như thế này:
PHP 5.3.6 (gần đây nhất tại thời điểm viết) mô tả hai hàm trong mã C gốc của chúng trong url.c tệp .
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);
}
Được rồi, vậy có gì khác ở đây?
Cả hai về bản chất đều gọi hai hàm nội bộ khác nhau tương ứng: php_raw_url_encode và php_url_encode
Vì vậy, hãy tìm kiếm các chức năng!
Hãy xem 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);
}
Và tất nhiên, 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;
}
Một chút kiến thức nhanh trước khi tôi tiến lên, EBCDIC là một bộ ký tự khác , tương tự ASCII, nhưng là một đối thủ cạnh tranh tổng thể. PHP cố gắng để đối phó với cả hai. Nhưng về cơ bản, điều này có nghĩa là byte EBCDIC 0x4c byte không phải là L
ASCII, nó thực sự là một <
. Tôi chắc rằng bạn nhìn thấy sự nhầm lẫn ở đây.
Cả hai chức năng này đều quản lý EBCDIC nếu máy chủ web đã xác định nó.
Ngoài ra, cả hai đều sử dụng một mảng các ký tự (kiểu chuỗi suy nghĩ) hexchars
để tìm kiếm một số giá trị, mảng được mô tả như sau:
/* 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";
Ngoài ra, các chức năng thực sự khác nhau và tôi sẽ giải thích chúng trong ASCII và EBCDIC.
Sự khác biệt trong ASCII:
URLENCODE:
- Tính độ dài bắt đầu / kết thúc của chuỗi đầu vào, phân bổ bộ nhớ
- Đi qua một vòng lặp while, tăng dần cho đến khi chúng ta đi đến cuối chuỗi
- Lấy nhân vật hiện tại
- Nếu ký tự bằng ASCII Char 0x20 (nghĩa là "khoảng trắng"), hãy thêm một
+
dấu hiệu vào chuỗi đầu ra.
- Nếu nó không phải là một khoảng trắng và nó cũng không phải là chữ và số (
isalnum(c)
), và cũng không phải và _
, -
hoặc .
ký tự, thì chúng ta, xuất một %
dấu hiệu đến vị trí mảng 0, thực hiện một mảng tìm đến hexchars
mảng để tìm kiếm os_toascii
mảng ( tìm kiếm mảng) một mảng từ Apache dịch mã char thành mã hex) cho khóa của c
(ký tự hiện tại), sau đó chúng ta dịch chuyển bit phải sang 4, gán giá trị đó cho ký tự 1 và đến vị trí 2, chúng ta gán cùng một tra cứu, ngoại trừ chúng ta tạo khuôn một logic và để xem nếu giá trị là 15 (0xF) và trả về 1 trong trường hợp đó hoặc 0 khác. Cuối cùng, bạn sẽ kết thúc với một cái gì đó được mã hóa.
- Nếu kết thúc nó không phải là một khoảng trắng, đó là chữ và số hoặc một trong các
_-.
ký tự, nó xuất ra chính xác nó là gì.
RAWURLENCODE:
- Phân bổ bộ nhớ cho chuỗi
- Lặp lại trên nó dựa trên độ dài được cung cấp trong lệnh gọi hàm (không được tính trong hàm như với URLENCODE).
Lưu ý: Nhiều lập trình viên có thể chưa bao giờ thấy một vòng lặp lặp theo cách này, nó hơi hack và không phải là quy ước tiêu chuẩn được sử dụng với hầu hết các vòng lặp, chú ý, nó gán x
và y
, kiểm tra thoát khi len
đạt 0, và tăng cả hai x
và y
. Tôi biết, đó không phải là những gì bạn mong đợi, nhưng đó là mã hợp lệ.
- Chỉ định ký tự hiện tại cho một vị trí ký tự phù hợp trong
str
.
- Nó kiểm tra xem ký tự hiện tại có phải là chữ và số hay một trong các
_-.
ký tự không và nếu không, chúng tôi thực hiện gần như cùng một nhiệm vụ với URLENCODE, trong đó nó tạo ra các tra cứu, tuy nhiên, chúng tôi tăng khác nhau, sử dụng y++
thay vì to[1]
, điều này là do các chuỗi đang được xây dựng theo những cách khác nhau, nhưng dù sao cũng đạt được cùng một mục tiêu.
- Khi vòng lặp hoàn thành và độ dài hết, Nó thực sự chấm dứt chuỗi, gán
\0
byte.
- Nó trả về chuỗi được mã hóa.
Sự khác biệt:
- UrlEncode kiểm tra không gian, gán dấu +, RawURLEncode thì không.
- UrlEncode không gán một
\0
byte cho chuỗi, RawUrlEncode thực hiện (đây có thể là điểm moot)
- Chúng lặp đi lặp lại một cách khác nhau, một người có thể dễ bị tràn với các chuỗi không đúng định dạng, tôi chỉ đề xuất điều này và tôi chưa thực sự điều tra.
Về cơ bản, chúng lặp đi lặp lại khác nhau, người ta gán dấu + trong trường hợp ASCII 20.
Sự khác biệt trong EBCDIC:
URLENCODE:
- Thiết lập lặp tương tự như với ASCII
- Vẫn dịch ký tự "dấu cách" thành dấu + . Lưu ý-- Tôi nghĩ rằng điều này cần phải được biên dịch trong EBCDIC hoặc bạn sẽ gặp phải một lỗi? Ai đó có thể chỉnh sửa và xác nhận điều này?
- Nó kiểm tra xem char hiện tại có phải là char trước hay không
0
, ngoại trừ là một .
hoặc -
, OR nhỏ hơn A
nhưng lớn hơn char 9
, OR lớn hơn Z
và nhỏ hơn a
nhưng không phải là a _
. HOẶC lớn hơn z
(vâng, EBCDIC hơi rối khi làm việc với). Nếu nó phù hợp với bất kỳ thứ nào trong số đó, hãy thực hiện tra cứu tương tự như trong phiên bản ASCII (nó không yêu cầu tra cứu trong os_toascii).
RAWURLENCODE:
- Thiết lập lặp tương tự như với ASCII
- Kiểm tra tương tự như được mô tả trong phiên bản EBCDIC của Mã hóa URL, ngoại trừ nếu nó lớn hơn
z
, nó sẽ loại trừ ~
khỏi mã hóa URL.
- Nhiệm vụ tương tự như ASCII RawUrlEncode
- Vẫn nối thêm
\0
byte vào chuỗi trước khi trả về.
Tóm tắt lớn
- Cả hai đều sử dụng cùng một bảng tra cứu hexchars
- URIEncode không chấm dứt một chuỗi với \ 0, raw không.
- Nếu bạn đang làm việc trong EBCDIC, tôi khuyên bạn nên sử dụng RawUrlEncode, vì nó quản lý
~
UrlEncode đó không ( đây là một vấn đề được báo cáo ). Điều đáng chú ý là ASCII và EBCDIC 0x20 đều là không gian.
- Chúng lặp đi lặp lại khác nhau, một cái có thể nhanh hơn, một cái có thể dễ bị khai thác dựa trên bộ nhớ hoặc chuỗi.
- URIEncode tạo khoảng trống vào
+
, RawUrlEncode tạo khoảng trống %20
thông qua tra cứu mảng.
Tuyên bố miễn trừ trách nhiệm: Tôi đã không chạm vào C trong nhiều năm và tôi đã không nhìn vào EBCDIC trong một thời gian thực sự dài. Nếu tôi sai ở đâu đó, hãy cho tôi biết.
Đề xuất thực hiện
Dựa trên tất cả những điều này, rawurlencode là cách để đi hầu hết thời gian. Như bạn thấy trong câu trả lời của Jonathan Fingerland, hãy kiên trì với nó trong hầu hết các trường hợp. Nó liên quan đến sơ đồ hiện đại cho các thành phần URI, trong đó như urlencode thực hiện mọi thứ theo cách trường học cũ, trong đó + có nghĩa là "không gian".
Nếu bạn đang cố gắng chuyển đổi giữa định dạng cũ và định dạng mới, hãy chắc chắn rằng mã của bạn không bị hỏng và biến thứ gì đó được giải mã + thành không gian bằng cách vô tình mã hóa hai lần hoặc các tình huống "oops" tương tự xung quanh điều này dung lượng / 20% / + vấn đề.
Tuy nhiên, nếu bạn đang làm việc trên một hệ thống cũ hơn với phần mềm cũ không thích định dạng mới, hãy sử dụng urlencode, tuy nhiên, tôi tin rằng% 20 sẽ thực sự tương thích ngược, vì theo tiêu chuẩn cũ 20% hoạt động, chỉ là không hoạt động ưa thích. Cung cấp cho nó một shot nếu bạn đang chơi xung quanh, cho chúng tôi biết làm thế nào nó làm việc cho bạn.
Về cơ bản, bạn nên gắn bó với bản thô, trừ khi hệ thống EBCDIC của bạn thực sự ghét bạn. Hầu hết các lập trình viên sẽ không bao giờ chạy vào EBCDIC trên bất kỳ hệ thống nào được thực hiện sau năm 2000, thậm chí là năm 1990 (theo quan điểm của tôi, nhưng vẫn có khả năng).