La prova è nel codice sorgente di PHP.
Ti guiderò attraverso un rapido processo su come scoprire questo genere di cose da solo in futuro ogni volta che vuoi. Abbi pazienza, ci sarà un sacco di codice sorgente C su cui puoi scorrere (lo spiego). Se vuoi rispolverare qualche C, un buon punto di partenza è il nostro SO wiki .
Scarica il sorgente (o usa http://lxr.php.net/ per sfogliarlo online), grep tutti i file per il nome della funzione, troverai qualcosa del genere:
PHP 5.3.6 (più recente al momento della scrittura), descrive le due funzioni nel loro codice C nativo nel 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);
}
Okay, quindi cosa c'è di diverso qui?
Entrambi in sostanza chiamano rispettivamente due diverse funzioni interne: php_raw_url_encode e php_url_encode
Quindi vai a cercare quelle funzioni!
Vediamo 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);
}
E, naturalmente, 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;
}
Un po 'di conoscenza prima di andare avanti, EBCDIC è un altro set di caratteri , simile a ASCII, ma un concorrente totale. PHP tenta di affrontare entrambi. Ma fondamentalmente, questo significa che il byte EBCDIC 0x4c non è L
in ASCII, in realtà è un <
. Sono sicuro che vedi la confusione qui.
Entrambe queste funzioni gestiscono EBCDIC se il web server lo ha definito.
Inoltre, entrambi utilizzano una matrice di caratteri (tipo di stringa di hexchars
ricerca ) per ottenere alcuni valori, la matrice è descritta come tale:
/* 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";
Oltre a ciò, le funzioni sono davvero diverse e le spiegherò in ASCII ed EBCDIC.
Differenze in ASCII:
UrlEncode:
- Calcola una lunghezza iniziale / finale della stringa di input, alloca memoria
- Cammina attraverso un ciclo while, con incrementi fino a raggiungere la fine della stringa
- Afferra il personaggio attuale
- Se il carattere è uguale al carattere ASCII 0x20 (ovvero uno "spazio"), aggiungi un
+
segno alla stringa di output.
- Se non è uno spazio, e non è nemmeno alfanumerico (
isalnum(c)
), e inoltre non è e _
, -
o .
carattere, quindi, produciamo un %
segno nella posizione 0 dell'array, facciamo un array cercando l' hexchars
array per una ricerca os_toascii
dell'array ( un array di Apache che traduce char in codice esadecimale) per la chiave di c
(il carattere attuale), spostiamo quindi bit a destra di 4, assegniamo quel valore al carattere 1 e alla posizione 2 assegniamo la stessa ricerca, tranne che preformiamo un logico e per vedere se il valore è 15 (0xF), e in tal caso restituisce un 1 o uno 0. Alla fine, finirai con qualcosa codificato.
- Se finisce non è uno spazio, è alfanumerico o uno dei
_-.
caratteri, produce esattamente quello che è.
rawurlencode:
- Alloca memoria per la stringa
- Passa da uno all'altro in base alla lunghezza fornita nella chiamata di funzione (non calcolata in funzione come con URLENCODE).
Nota: molti programmatori probabilmente non hanno mai visto un ciclo for iterare in questo modo, è un po 'hacker e non la convenzione standard usata con la maggior parte dei for-loop, presta attenzione, assegna x
e y
, controlla l'uscita per len
raggiungere lo 0 e incrementa entrambi x
e y
. Lo so, non è quello che ti aspetteresti, ma è un codice valido.
- Assegna il personaggio attuale a una posizione del personaggio corrispondente in
str
.
- Verifica se il carattere attuale è alfanumerico o uno dei
_-.
caratteri e, in caso contrario, eseguiamo quasi la stessa assegnazione di URLENCODE in cui preforma le ricerche, tuttavia, aumentiamo in modo diverso, utilizzando y++
anziché anziché to[1]
, perché le stringhe vengono costruite in diversi modi, ma alla fine raggiungono lo stesso obiettivo.
- Quando il ciclo è terminato e la lunghezza è passata, termina effettivamente la stringa, assegnando il
\0
byte.
- Restituisce la stringa codificata.
differenze:
- UrlEncode verifica lo spazio, assegna un segno +, RawURLEncode no.
- UrlEncode non assegna un
\0
byte alla stringa, RawUrlEncode lo fa (potrebbe trattarsi di un punto controverso)
- Esse differiscono in modo diverso, si potrebbe essere inclini a traboccare di stringhe malformate, sto solo suggerendo questo e non ho effettivamente indagato.
Fondamentalmente ripetono diversamente, si assegna un segno + in caso di ASCII 20.
Differenze in EBCDIC:
UrlEncode:
- Stessa configurazione dell'iterazione come con ASCII
- Sto ancora traducendo il carattere "spazio" in un segno + . Nota-- Penso che questo debba essere compilato in EBCDIC o finirai con un bug? Qualcuno può modificarlo e confermarlo?
- Verifica se il carattere attuale è un carattere prima
0
, ad eccezione di un .
o -
, OR minore di A
ma maggiore di char 9
, OR maggiore di Z
e minore di a
ma non a _
. O maggiore di z
(sì, EBCDIC è un po 'incasinato con cui lavorare). Se corrisponde a uno di questi, esegui una ricerca simile a quella della versione ASCII (semplicemente non richiede una ricerca in os_toascii).
rawurlencode:
- Stessa configurazione dell'iterazione come con ASCII
- Stesso controllo descritto nella versione EBCDIC di URL Encode, con l'eccezione che se è maggiore di
z
, si esclude ~
dalla codifica URL.
- Stessa assegnazione di ASCII RawUrlEncode
- Ancora aggiungendo il
\0
byte alla stringa prima di restituirlo.
Sommario generale
- Entrambi usano la stessa tabella di ricerca hexchars
- URIEncode non termina una stringa con \ 0, raw lo fa.
- Se lavori in EBCDIC, suggerirei di utilizzare RawUrlEncode, poiché gestisce
~
UrlEncode ( questo è un problema segnalato ). Vale la pena notare che ASCII e EBCDIC 0x20 sono entrambi spazi.
- Iterano in modo diverso, uno può essere più veloce, uno può essere incline alla memoria o agli exploit basati su stringa.
- URIEncode crea uno spazio
+
, RawUrlEncode crea uno spazio %20
tramite ricerche di array.
Disclaimer: non tocco C da anni e non guardo EBCDIC da molto tempo. Se sbaglio da qualche parte, fammi sapere.
Implementazioni suggerite
Sulla base di tutto ciò, rawurlencode è la strada da percorrere per la maggior parte del tempo. Come vedi nella risposta di Jonathan Fingland, segui la maggior parte dei casi. Si occupa del moderno schema per i componenti URI, dove come urlencode fa le cose alla vecchia maniera, dove + significava "spazio".
Se stai cercando di convertire tra il vecchio formato e i nuovi formati, assicurati che il tuo codice non esca e trasforma qualcosa che è un segno + decodificato in uno spazio accidentalmente con doppia codifica o scenari simili "oops" attorno a questo spazio / 20% / + problema.
Se stai lavorando su un sistema più vecchio con un software più vecchio che non preferisce il nuovo formato, mantieni il codice url, tuttavia, credo che% 20 sarà effettivamente retrocompatibile, poiché secondo il vecchio standard% 20 funzionava, semplicemente non lo era preferito. Provalo se sei pronto per giocare, facci sapere come ha funzionato per te.
Fondamentalmente, dovresti rimanere con raw, a meno che il tuo sistema EBCDIC non ti odia davvero. La maggior parte dei programmatori non si imbatterà mai in EBCDIC su alcun sistema realizzato dopo l'anno 2000, forse persino il 1990 (questo è un passo avanti, ma probabilmente secondo me).