La preuve est dans le code source de PHP.
Je vais vous guider à travers un processus rapide sur la façon de découvrir ce genre de chose par vous-même à l'avenir à tout moment. Soyez avec moi, il y aura beaucoup de code source C que vous pouvez parcourir (je l'explique). Si vous voulez rafraîchir certains C, un bon endroit pour commencer est notre wiki SO .
Téléchargez la source (ou utilisez http://lxr.php.net/ pour la parcourir en ligne), grep tous les fichiers pour le nom de la fonction, vous trouverez quelque chose comme ceci:
PHP 5.3.6 ( le plus récent au moment de la rédaction) décrit les deux fonctions dans leur code natif C dans le fichier 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);
}
D'accord, alors qu'est-ce qui est différent ici?
Ils appellent tous deux essentiellement deux fonctions internes différentes respectivement: php_raw_url_encode et php_url_encode
Alors allez chercher ces fonctions!
Regardons 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);
}
Et bien sûr, 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 peu de connaissances avant d'aller de l'avant, EBCDIC est un autre jeu de caractères , similaire à ASCII, mais un concurrent total. PHP tente de gérer les deux. Mais en gros, cela signifie que l'octet EBCDIC 0x4c n'est pas L
en ASCII, c'est en fait un <
. Je suis sûr que vous voyez la confusion ici.
Ces deux fonctions gèrent EBCDIC si le serveur Web l'a défini.
En outre, ils utilisent tous les deux un tableau de recherche de caractères (pensez au type de chaîne) hexchars
pour obtenir certaines valeurs, le tableau est décrit comme tel:
/* 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";
Au-delà de cela, les fonctions sont vraiment différentes, et je vais les expliquer en ASCII et EBCDIC.
Différences en ASCII:
URLENCODE:
- Calcule une longueur de début / fin de la chaîne d'entrée, alloue de la mémoire
- Parcourt une boucle while, incrémente jusqu'à atteindre la fin de la chaîne
- Saisit le personnage actuel
- Si le caractère est égal au caractère ASCII 0x20 (c'est-à-dire un "espace"), ajoutez un
+
signe à la chaîne de sortie.
- Si ce n'est pas un espace, et ce n'est pas non plus alphanumérique (
isalnum(c)
), et n'est pas non plus et _
, -
ou un .
caractère, alors nous, sortons un %
signe à la position 0 du tableau, faisons un hexchars
tableau pour rechercher le os_toascii
tableau ( un tableau d' Apache qui traduit char en code hexadécimal) pour la clé de c
(le caractère actuel), nous décalons ensuite bit à droite de 4, affectons cette valeur au caractère 1, et à la position 2 nous attribuons la même recherche, sauf que nous préformons un logique et pour voir si la valeur est 15 (0xF), et retourner un 1 dans ce cas, ou un 0 sinon. À la fin, vous vous retrouverez avec quelque chose d'encodé.
- S'il finit par ne pas être un espace, c'est alphanumérique ou l'un des
_-.
caractères, il sort exactement ce que c'est.
RAWURLENCODE:
- Alloue de la mémoire pour la chaîne
- Itère dessus en fonction de la longueur fournie dans l'appel de fonction (non calculé dans la fonction comme avec URLENCODE).
Remarque: De nombreux programmeurs n'ont probablement jamais vu d'itération de boucle for de cette façon, c'est quelque peu hack et non la convention standard utilisée avec la plupart des boucles for, faites attention, il attribue x
et y
vérifie la sortie en len
atteignant 0 et incrémente les deux x
et y
. Je sais, ce n'est pas ce que vous attendez, mais c'est un code valide.
- Attribue le caractère actuel à une position de caractère correspondante dans
str
.
- Il vérifie si le caractère actuel est alphanumérique ou l'un des
_-.
caractères, et s'il ne l'est pas, nous faisons presque la même affectation qu'avec URLENCODE où il préforme les recherches, cependant, nous incrémentons différemment, en utilisant y++
plutôt que to[1]
, c'est parce que le les cordes sont construites de différentes manières, mais atteignent le même objectif à la fin de toute façon.
- Lorsque la boucle est terminée et que la longueur a disparu, il termine en fait la chaîne, attribuant l'
\0
octet.
- Il renvoie la chaîne codée.
Différences:
- UrlEncode vérifie l'espace, attribue un signe +, RawURLEncode non.
- UrlEncode n'affecte pas d'
\0
octet à la chaîne, RawUrlEncode le fait (cela peut être un point discutable)
- Ils itèrent différemment, on peut avoir tendance à déborder avec des chaînes mal formées, je suggère simplement cela et je n'ai pas réellement enquêté.
Ils itèrent fondamentalement différemment, on attribue un signe + en cas d'ASCII 20.
Différences dans EBCDIC:
URLENCODE:
- Même configuration d'itération qu'avec ASCII
- Toujours traduire le caractère "espace" en signe + . Remarque - Je pense que cela doit être compilé dans EBCDIC ou vous vous retrouverez avec un bogue? Quelqu'un peut-il modifier et confirmer cela?
- Il vérifie si le caractère actuel est un caractère avant
0
, à l'exception d'être un .
ou -
, OU inférieur A
mais supérieur à caractère 9
, OU supérieur Z
et inférieur à a
mais non a _
. OU supérieur à z
(ouais, EBCDIC est un peu foiré de travailler avec). S'il correspond à l'un d'entre eux, effectuez une recherche similaire à celle trouvée dans la version ASCII (cela ne nécessite tout simplement pas de recherche dans os_toascii).
RAWURLENCODE:
- Même configuration d'itération qu'avec ASCII
- Même vérification que celle décrite dans la version EBCDIC du codage d'URL, à l'exception que si elle est supérieure à
z
, elle exclut ~
du codage d'URL.
- Même affectation que le code ASCII RawUrlEncode
- Ajoutant toujours l'
\0
octet à la chaîne avant le retour.
Grand résumé
- Les deux utilisent la même table de recherche hexchars
- URIEncode ne termine pas une chaîne avec \ 0, brut le fait.
- Si vous travaillez dans EBCDIC, je vous suggère d'utiliser RawUrlEncode, car il gère ce
~
que UrlEncode ne fait pas ( il s'agit d'un problème signalé ). Il convient de noter que ASCII et EBCDIC 0x20 sont tous deux des espaces.
- Ils itèrent différemment, l'un peut être plus rapide, l'autre peut être sujet à des exploits basés sur la mémoire ou les chaînes.
- URIEncode crée un espace
+
, RawUrlEncode crée un espace %20
via des recherches de tableau.
Avertissement: je n'ai pas touché C depuis des années et je n'ai pas regardé EBCDIC depuis très très longtemps. Si je me trompe quelque part, faites le moi savoir.
Implémentations suggérées
Sur la base de tout cela, le rawurlencode est la voie à suivre la plupart du temps. Comme vous le voyez dans la réponse de Jonathan Fingland, respectez-la dans la plupart des cas. Il traite du schéma moderne pour les composants URI, où comme l'urlencode fait les choses à l'ancienne, où + signifiait «espace».
Si vous essayez de convertir entre l'ancien format et les nouveaux formats, assurez-vous que votre code ne se gâte pas et ne transforme pas quelque chose qui est un signe + décodé en espace en double-encodant accidentellement, ou des scénarios similaires "oops" autour de ce espace / 20% / + problème.
Si vous travaillez sur un système plus ancien avec un logiciel plus ancien qui ne préfère pas le nouveau format, restez avec le code url, cependant, je pense que% 20 sera en fait rétrocompatible, comme sous l'ancien standard% 20 fonctionnait, n'était tout simplement pas préféré. Essayez-le si vous êtes prêt à jouer, faites-nous savoir comment cela a fonctionné pour vous.
Fondamentalement, vous devriez vous en tenir au raw, à moins que votre système EBCDIC ne vous déteste vraiment. La plupart des programmeurs ne rencontreront jamais EBCDIC sur un système fabriqué après l'an 2000, peut-être même 1990 (c'est poussant, mais toujours probable à mon avis).