Bukti ada dalam kode sumber PHP.
Saya akan membawa Anda melalui proses cepat tentang bagaimana mencari tahu hal semacam ini sendiri di masa depan kapan saja Anda inginkan. Bersabarlah, akan ada banyak kode sumber C yang bisa Anda lewati (saya jelaskan). Jika Anda ingin memoles C, tempat yang baik untuk memulai adalah SO wiki kami .
Unduh sumbernya (atau gunakan http://lxr.php.net/ untuk menjelajahnya secara online), ambil semua file untuk nama fungsi, Anda akan menemukan sesuatu seperti ini:
PHP 5.3.6 (terbaru pada saat penulisan) menjelaskan dua fungsi dalam kode C asli mereka dalam file 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);
}
Oke, jadi apa yang berbeda di sini?
Keduanya sama-sama memanggil dua fungsi internal yang berbeda: php_raw_url_encode dan php_url_encode
Jadi pergi mencari fungsi-fungsi itu!
Mari kita lihat 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);
}
Dan tentu saja, 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;
}
Satu pengetahuan singkat sebelum saya bergerak maju, EBCDIC adalah rangkaian karakter lain , mirip dengan ASCII, tetapi merupakan pesaing total. PHP mencoba untuk menangani keduanya. Tetapi pada dasarnya, ini berarti byte EBCDIC 0x4c byte bukan L
di ASCII, itu sebenarnya a <
. Saya yakin Anda melihat kebingungan di sini.
Kedua fungsi ini mengelola EBCDIC jika server web telah menetapkannya.
Selain itu, keduanya menggunakan array karakter chars (think string type) hexchars
untuk mendapatkan beberapa nilai, array digambarkan seperti itu:
/* 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";
Selain itu, fungsinya sangat berbeda, dan saya akan menjelaskannya dalam ASCII dan EBCDIC.
Perbedaan dalam ASCII:
URLENCODE:
- Menghitung panjang mulai / akhir dari string input, mengalokasikan memori
- Berjalan melalui loop sementara, bertambah hingga kami mencapai akhir string
- Raih karakter yang sekarang
- Jika karakter sama dengan ASCII Char 0x20 (yaitu, "spasi"), tambahkan
+
tanda ke string output.
- Jika bukan spasi, dan juga bukan alfanumerik (
isalnum(c)
), dan juga bukan dan _
,, -
atau .
karakter, maka kita, menampilkan %
tanda ke posisi array 0, melakukan pencarian array ke hexchars
array untuk pencarian os_toascii
array () sebuah array dari Apache yang menerjemahkan char ke hex code) untuk kunci c
(karakter sekarang), kita kemudian menggeser bitwise ke kanan dengan 4, menetapkan nilai itu ke karakter 1, dan ke posisi 2 kita menetapkan pencarian yang sama, kecuali kita membentuk sebelumnya a logis dan untuk melihat apakah nilainya 15 (0xF), dan mengembalikan 1 dalam kasus itu, atau 0 sebaliknya. Pada akhirnya, Anda akan berakhir dengan sesuatu yang dikodekan.
- Jika pada akhirnya itu bukan spasi, itu alfanumerik atau salah satu
_-.
karakter, itu menghasilkan persis apa itu.
RAWURLENCODE:
- Mengalokasikan memori untuk string
- Iterasi berdasarkan panjang yang disediakan dalam panggilan fungsi (tidak dihitung dalam fungsi seperti dengan URLENCODE).
Catatan: Banyak programmer mungkin belum pernah melihat for loop iterate dengan cara ini, ini agak peretasan dan bukan konvensi standar yang digunakan dengan sebagian besar untuk-loop, perhatikan, itu menugaskan x
dan y
, memeriksa untuk keluar pada len
mencapai 0, dan meningkatkan keduanya x
dan y
. Saya tahu, itu bukan yang Anda harapkan, tapi itu kode yang valid.
- Tetapkan karakter sekarang ke posisi karakter yang cocok di
str
.
- Ia memeriksa apakah karakter saat ini adalah alfanumerik, atau salah satu
_-.
karakter, dan jika tidak, kami melakukan tugas yang hampir sama seperti dengan URLENCODE di mana ia membentuk pencarian sebelumnya, namun, kami menambah secara berbeda, menggunakan y++
daripada to[1]
, ini karena string sedang dibangun dengan cara yang berbeda, tetapi mencapai tujuan yang sama pada akhirnya.
- Ketika loop selesai dan panjangnya hilang, itu benar-benar mengakhiri string, menetapkan
\0
byte.
- Ini mengembalikan string yang disandikan.
Perbedaan:
- UrlEncode memeriksa ruang, memberikan tanda +, RawURLEncode tidak.
- UrlEncode tidak menetapkan
\0
byte ke string, RawUrlEncode tidak (ini mungkin titik diperdebatkan)
- Mereka beralih secara berbeda, seseorang mungkin cenderung meluap dengan string yang cacat, saya hanya menyarankan ini dan saya belum benar - benar menyelidiki.
Mereka pada dasarnya beralih secara berbeda, seseorang memberikan tanda + pada acara ASCII 20.
Perbedaan dalam EBCDIC:
URLENCODE:
- Pengaturan iterasi yang sama dengan ASCII
- Masih menerjemahkan karakter "spasi" menjadi tanda + . Catatan - Saya pikir ini perlu dikompilasi dalam EBCDIC atau Anda akan berakhir dengan bug? Dapatkah seseorang mengedit dan mengkonfirmasi ini?
- Hal memeriksa apakah char sekarang adalah char sebelum
0
, dengan pengecualian menjadi .
atau -
, OR kurang dari A
namun lebih besar dari arang 9
, OR lebih besar dari Z
dan kurang dari a
tapi bukan _
. ATAU lebih besar dari z
(yeah, EBCDIC agak kacau untuk bekerja dengannya). Jika cocok dengan semua itu, lakukan pencarian serupa seperti yang ditemukan di versi ASCII (hanya saja tidak memerlukan pencarian di os_toascii).
RAWURLENCODE:
- Pengaturan iterasi yang sama dengan ASCII
- Pemeriksaan yang sama seperti yang dijelaskan dalam versi EBCDIC dari URL Encode, dengan pengecualian jika lebih besar dari
z
itu, tidak termasuk ~
dari URL encode.
- Tugas yang sama dengan kode ASCII RawUrlEncode
- Masih menambahkan
\0
byte ke string sebelum kembali.
Ringkasan Besar
- Keduanya menggunakan tabel pencarian hexchars yang sama
- URIEncode tidak mengakhiri string dengan \ 0, raw tidak.
- Jika Anda bekerja di EBCDIC, saya sarankan menggunakan RawUrlEncode, karena ia mengelola
~
UrlEncode yang tidak ( ini masalah yang dilaporkan ). Perlu dicatat bahwa ASCII dan EBCDIC 0x20 sama-sama ruang.
- Mereka beralih secara berbeda, satu mungkin lebih cepat, satu mungkin rentan terhadap eksploitasi berbasis memori atau string.
- URIEncode membuat spasi menjadi
+
, RawUrlEncode membuat spasi menjadi %20
melalui pencarian array.
Penafian: Saya belum menyentuh C selama bertahun-tahun, dan saya belum melihat EBCDIC dalam waktu yang sangat lama. Jika saya salah di suatu tempat, beri tahu saya.
Implementasi yang disarankan
Berdasarkan semua ini, rawurlencode adalah cara untuk pergi sebagian besar waktu. Seperti yang Anda lihat dalam jawaban Jonathan Fingland, tetap gunakan dalam kebanyakan kasus. Ini berkaitan dengan skema modern untuk komponen URI, di mana urlencode melakukan sesuatu dengan cara lama, di mana + berarti "ruang."
Jika Anda mencoba mengonversi antara format lama dan format baru, pastikan kode Anda tidak naik dan mengubah sesuatu yang diberi tanda + diterjemahkan menjadi ruang dengan penyandian ganda tanpa sengaja, atau skenario "oops" serupa di sekitar ini ruang / 20% / + masalah.
Jika Anda bekerja pada sistem yang lebih lama dengan perangkat lunak yang lebih tua yang tidak menyukai format baru, tetap menggunakan urlencode, namun, saya yakin% 20 akan benar-benar kompatibel, seperti di bawah standar lama% 20 bekerja, hanya saja tidak lebih disukai. Cobalah jika Anda ingin bermain-main, beri tahu kami cara kerjanya untuk Anda.
Pada dasarnya, Anda harus tetap menggunakan mentah, kecuali jika sistem EBCDIC Anda benar-benar membenci Anda. Sebagian besar programmer tidak akan pernah mengalami EBCDIC pada sistem apa pun yang dibuat setelah tahun 2000, bahkan mungkin tahun 1990 (itu mendorong, tetapi masih mungkin menurut saya).