La prueba está en el código fuente de PHP.
Te guiaré a través de un proceso rápido de cómo descubrir este tipo de cosas por tu cuenta en el futuro en cualquier momento que desees. Tenga paciencia conmigo, habrá un montón de código fuente C que puede leer (lo explico). Si desea repasar un poco de C, un buen lugar para comenzar es nuestra wiki de SO .
Descargue la fuente (o use http://lxr.php.net/ para navegar en línea), seleccione todos los archivos para el nombre de la función, encontrará algo como esto:
PHP 5.3.6 (más reciente en el momento de escribir esto) describe las dos funciones en su código nativo de C en el archivo 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);
}
Bien, entonces, ¿qué hay de diferente aquí?
Ambos esencialmente llaman a dos funciones internas diferentes respectivamente: php_raw_url_encode y php_url_encode
¡Así que ve a buscar esas funciones!
Veamos 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);
}
Y, por supuesto, 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 poco de conocimiento rápido antes de avanzar, EBCDIC es otro conjunto de caracteres , similar a ASCII, pero un competidor total. PHP intenta lidiar con ambos. Pero básicamente, esto significa que el byte EBCDIC 0x4c no está L
en ASCII, en realidad es un <
. Estoy seguro de que ves la confusión aquí.
Ambas funciones administran EBCDIC si el servidor web lo ha definido.
Además, ambos usan una matriz de caracteres (piense en el tipo de cadena) hexchars
para obtener algunos valores, la matriz se describe como tal:
/* 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";
Más allá de eso, las funciones son realmente diferentes, y las voy a explicar en ASCII y EBCDIC.
Diferencias en ASCII:
URLENCODE:
- Calcula una longitud de inicio / fin de la cadena de entrada, asigna memoria
- Camina a través de un ciclo while, incrementos hasta llegar al final de la cadena
- Agarra el personaje actual
- Si el carácter es igual a ASCII Char 0x20 (es decir, un "espacio"), agregue un
+
signo a la cadena de salida.
- Si no es un espacio, y tampoco es alfanumérico (
isalnum(c)
), y tampoco es y _
, -
o .
carácter, entonces, enviamos un %
signo a la posición 0 de la matriz, buscamos una hexchars
matriz para buscar una os_toascii
matriz ( una matriz de Apache que traduce char a código hexadecimal) para la clave de c
(el carácter actual), luego cambiamos bit a la derecha por 4, asignamos ese valor al carácter 1, y a la posición 2 asignamos la misma búsqueda, excepto que preformamos un lógico y para ver si el valor es 15 (0xF), y devolver un 1 en ese caso, o un 0 en caso contrario. Al final, terminarás con algo codificado.
- Si termina no es un espacio, es alfanumérico o uno de los
_-.
caracteres, genera exactamente lo que es.
CÓDIGO SIN PROCESAR:
- Asigna memoria para la cadena
- Se repite en función de la longitud proporcionada en la llamada a la función (no se calcula en la función como con URLENCODE).
Nota: Muchos programadores probablemente nunca hayan visto un bucle for iterar de esta manera, es algo hackeo y no es la convención estándar utilizada con la mayoría de los bucles for, presta atención, asigna x
y y
comprueba la salida al len
llegar a 0, e incrementa ambos x
y y
. Lo sé, no es lo que esperarías, pero es un código válido.
- Asigna el personaje actual a una posición de personaje coincidente en
str
.
- Comprueba si el carácter actual es alfanumérico, o uno de los
_-.
caracteres, y si no lo es, hacemos casi la misma asignación que con URLENCODE donde realiza búsquedas, sin embargo, incrementamos de manera diferente, usando en y++
lugar de to[1]
, esto es porque el Las cadenas se construyen de diferentes maneras, pero de todos modos alcanzan el mismo objetivo al final.
- Cuando finaliza el ciclo y desaparece la longitud, en realidad termina la cadena, asignando el
\0
byte.
- Devuelve la cadena codificada.
Diferencias:
- UrlEncode busca espacio, asigna un signo +, RawURLEncode no.
- UrlEncode no asigna un
\0
byte a la cadena, RawUrlEncode sí (esto puede ser un punto discutible)
- Se repiten de manera diferente, uno puede ser propenso a desbordarse con cadenas malformadas, simplemente sugiero esto y en realidad no he investigado.
Básicamente iteran de manera diferente, uno asigna un signo + en el caso de ASCII 20.
Diferencias en EBCDIC:
URLENCODE:
- La misma configuración de iteración que con ASCII
- Aún traduciendo el carácter "espacio" a un signo + . Nota: ¿creo que esto debe compilarse en EBCDIC o terminará con un error? ¿Alguien puede editar y confirmar esto?
- Comprueba si el char actual es un char antes
0
, con la excepción de ser un .
o -
, O menor que A
pero mayor que char 9
, O mayor que Z
y menor que a
pero no a _
. O mayor que z
(sí, EBCDIC es un poco complicado para trabajar). Si coincide con alguno de ellos, realice una búsqueda similar a la que se encuentra en la versión ASCII (simplemente no requiere una búsqueda en os_toascii).
CÓDIGO SIN PROCESAR:
- La misma configuración de iteración que con ASCII
- La misma verificación que se describe en la versión EBCDIC de URL Encode, con la excepción de que si es mayor que
z
, se excluye ~
de la codificación URL.
- Misma asignación que el ASCII RawUrlEncode
- Aún agregando el
\0
byte a la cadena antes de regresar.
Gran resumen
- Ambos usan la misma tabla de búsqueda hexchars
- URIEncode no termina una cadena con \ 0, sin procesar.
- Si está trabajando en EBCDIC, sugeriría usar RawUrlEncode, ya que gestiona
~
que UrlEncode no lo haga ( este es un problema reportado ). Vale la pena señalar que ASCII y EBCDIC 0x20 son ambos espacios.
- Se iteran de manera diferente, uno puede ser más rápido, uno puede ser propenso a la memoria o exploits basados en cadenas.
- URIEncode crea un espacio en
+
, RawUrlEncode crea un espacio en las %20
búsquedas de matriz.
Descargo de responsabilidad: no he tocado C en años, y no he visto EBCDIC en mucho, mucho tiempo. Si me equivoco en alguna parte, avísame.
Implementaciones sugeridas
Basado en todo esto, el código rawurlen es el camino a seguir la mayor parte del tiempo. Como puede ver en la respuesta de Jonathan Fingland, sígalo en la mayoría de los casos. Se trata del esquema moderno para los componentes de URI, donde como urlencode hace las cosas a la antigua usanza, donde + significa "espacio".
Si está tratando de convertir entre el formato anterior y el nuevo, asegúrese de que su código no aumente y convierta algo que es un signo decodificado + en un espacio mediante la doble codificación accidental o escenarios similares de "oops" alrededor de esto espacio / 20% / + tema.
Si está trabajando en un sistema más antiguo con un software más antiguo que no prefiere el nuevo formato, siga con urlencode, sin embargo, creo que% 20 en realidad será compatible con versiones anteriores, ya que según el antiguo estándar% 20 funcionó, simplemente no era privilegiado. Pruébalo si estás dispuesto a jugar, dinos cómo te fue.
Básicamente, debe seguir con raw, a menos que su sistema EBCDIC realmente lo odie. La mayoría de los programadores nunca se encontrarán con EBCDIC en ningún sistema fabricado después del año 2000, tal vez incluso 1990 (eso es difícil, pero aún es probable en mi opinión).