web-dev-qa-db-fra.com

urlencode vs rawurlencode?

Si je veux créer une URL en utilisant une variable, j'ai deux choix pour encoder la chaîne. urlencode() et rawurlencode().

Quelles sont exactement les différences et lesquelles sont préférées?

368
Gary Willoughby

Cela dépendra de votre but. Si l'interopérabilité avec d'autres systèmes est importante, il semble alors qu'il soit préférable d'utiliser du code rawurlencod La seule exception concerne les systèmes hérités qui s'attendent à ce que la chaîne de requête suive le style de codage de formulaire des espaces codés sous la forme + au lieu de% 20 (auquel cas vous avez besoin d'un code urlencode).

rawurlencode suit la RFC 1738 avant le PHP 5.3.0 et la RFC 3986 ensuite (voir http: // us2. php.net/manual/en/function.rawurlencode.php )

Renvoie une chaîne dans laquelle tous les caractères non alphanumériques, à l'exception de -_. ~, Ont été remplacés par un signe de pourcentage (%) suivi de deux chiffres hexadécimaux. Il s'agit de l'encodage décrit dans la "RFC 3986 pour empêcher les caractères littéraux d'être interprétés en tant que délimiteurs d'URL spéciaux et pour protéger les URL contre les altérations causées par les supports de transmission avec conversion de caractères (comme certains systèmes de messagerie).

Note sur RFC 3986 vs 1738. rawurlencode avant PHP 5.3 encodait le caractère tilde (~) conformément à RFC 1738. À partir de PHP 5.3, cependant, rawurlencode suit RFC 3986, qui ne nécessite pas d'encodage. caractères tilde.

urlencode code les espaces en tant que signes plus (et non en tant que %20 comme dans rawurlencode) (voir http: //us2.php .net/manual/en/function.urlencode.php )

Retourne une chaîne contenant tous les caractères non alphanumériques sauf -_. ont été remplacés par un signe de pourcentage (%) suivi de deux chiffres hexadécimaux et d'espaces codés sous la forme de signes plus (+). Il est codé de la même manière que les données postées d’un formulaire WWW, comme dans le type de média application/x-www-form-urlencoded. Cela diffère du codage "RFC 3986 (voir rawurlencode ()) en ce que, pour des raisons historiques, les espaces sont codés sous la forme de signes plus (+).

Cela correspond à la définition de l'application/x-www-form-urlencoded in RFC 1866 .

Lecture supplémentaire:

Vous voudrez peut-être aussi voir la discussion sur http://bytes.com/groups/php/5624-urlencode-vs-rawurlencode .

En outre, RFC 2396 vaut le détour. La RFC 2396 définit la syntaxe URI valide. La partie principale qui nous intéresse est tirée du composant de requête 3.4:

Dans un composant de requête, les caractères ";", "/", "?", ":", "@",
"&", "=", "+", ",", and "$"
sont réservés.

Comme vous pouvez le constater, le + est un caractère réservé dans la chaîne de requête et doit donc être codé conformément à la norme RFC 3986 (comme dans le code rawurlencode).

314
Jonathan Fingland

La preuve est dans le code source de PHP.

Je vais vous expliquer rapidement comment trouver ce genre de choses vous-même à l'avenir, quand vous le souhaitez. Ours avec moi, il y aura beaucoup de code source C que vous pouvez survoler (je l'explique). Si vous voulez rafraîchir un peu le langage C, un bon point de départ 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 (la plus récente au moment de l'écriture) décrit les deux fonctions dans leur code C natif 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);
}

Ok, alors qu'est-ce qui est différent ici?

Ils appellent tous deux deux fonctions internes différentes: 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 connaissance avant de continuer, EBCDIC est un autre jeu de caractères , similaire à ASCII, mais un concurrent total. PHP tente de gérer les deux. En gros, cela signifie octet octet EBCDIC 0x4c octet n’est pas le 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 deux un tableau de caractères (type de chaîne) hexchars look-up pour obtenir des valeurs, le tableau est décrit comme suit:

/* 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 dans 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'à la fin de la chaîne
  • Saisit le personnage actuel
  • Si le caractère est égal à ASCII Char 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 . caractère, alors nous produisons un % signe à la position 0 du tableau, effectuez une recherche dans le tableau hexchars pour une recherche de os_toascii tableau (un tableau de Apache qui traduit ​​char en code hexadécimal) pour la touche de c (le caractère actuel), nous décalons ensuite de 4 bits à droite, assignons cette valeur au caractère 1, et à la position 2, nous assignons la même recherche, sauf que nous effectuons une pour voir si la valeur est 15 (0xF) et renvoyer un 1 dans ce cas, ou un 0 dans les autres cas. À la fin, vous obtiendrez quelque chose d'encodé.
  • Si cela finit, ce n'est pas un espace, c'est alphanumérique ou un des caractères _-., il affichera 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 une boucle for itérer de cette façon, c'est un peu bidon et pas la convention standard utilisée avec la plupart des boucles for loops, faites attention , il affecte x et y, vérifie la sortie sur len jusqu’à 0 et incrémente x et y. Je sais, ce n'est pas ce à quoi vous vous attendiez, mais c'est un code valide.

  • Assigne 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 si ce n'est pas le cas, nous faisons presque la même affectation que pour URLENCODE, où il effectue des recherches, mais nous incrémentons différemment, en utilisant y++ plutôt que to[1], c'est parce que les chaînes sont construites de différentes manières, mais atteignent le même objectif à la fin.
  • Lorsque la boucle est terminée et que la longueur est épuisée, il termine la chaîne en affectant l'octet \0.
  • Il retourne la chaîne encodée.

Différences:

  • UrlEncode vérifie l'espace, attribue un signe +, mais pas RawURLEncode.
  • UrlEncode n'attribue pas d'octet \0 à la chaîne, contrairement à RawUrlEncode (cela peut être un point discutable)
  • Ils itérèrent différemment, on peut être enclin à déborder avec des chaînes malformées, je ne fait que suggérer ceci et moi 't réellement enquêté.

En gros, ils itèrent différemment, on attribue un signe + dans le cas de ASCII 20.

Différences dans EBCDIC:

URLENCODE:

  • Même configuration d'itération qu'avec ASCII
  • Toujours en train de traduire le caractère "espace" en signe + . Remarque - Je pense que cela doit être compilé dans EBCDIC ou vous allez vous retrouver avec un bogue? Est-ce que quelqu'un peut éditer et confirmer ceci?
  • Il vérifie si le caractère présent est un caractère avant 0, à l'exception d'être . ou -, OU inférieur à A mais supérieur à char 9, OU supérieur à Z et inférieur à a mais pas à _. OU supérieur à z (ouais, EBCDIC est un peu foiré pour travailler avec). Si cela correspond à l'un de ceux-ci, effectuez une recherche similaire à celle trouvée dans la version ASCII (elle ne nécessite 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 d’URL Encode, à l’exception que si elle est supérieure à z, elle exclut ~ de l’encodage URL.
  • Même affectation que le ASCII RawUrlEncode
  • Toujours en ajoutant l'octet \0 à la chaîne avant le renvoi.

Grand résumé

  • Les deux utilisent la même table de recherche hexchars
  • URIEncode ne termine pas une chaîne avec\0, raw le fait.
  • Si vous travaillez dans EBCDIC, je suggérerais d'utiliser RawUrlEncode, car il gère le ~, contrairement à UrlEncode ( c'est un problème signalé ). Il convient de noter que ASCII et EBCDIC 0x20 sont deux espaces.
  • Ils itèrent différemment, on peut être plus rapide, on peut être sujet à des exploits basés sur la mémoire ou sur des chaînes.
  • URIEncode crée un espace dans +, RawUrlEncode crée un espace dans %20 via des recherches dans un tableau.

Avertissement: Je n'ai pas touché à C depuis des années et je n'ai pas examiné EBCDIC depuis très longtemps. Si je me trompe quelque part, faites le moi savoir.

Implémentations suggérées

En se basant sur tout cela, rawurlencode est la voie à suivre la plupart du temps. Comme vous le voyez dans la réponse de Jonathan Fingland, respectez-le dans la plupart des cas. Il traite du schéma moderne pour les composants URI, où urlencode fait les choses à l'ancienne, où + voulait dire "espace".

Si vous essayez de convertir entre l'ancien et le nouveau format, assurez-vous que votre code ne va pas bien et transformez quelque chose qui est un signe décodé + en un espace par un double encodage accidentel, ou des scénarios similaires "oops" autour de cela. espace/20%/+ numéro.

Si vous travaillez sur un ancien système avec un logiciel plus ancien qui ne préfère pas le nouveau format, restez-en à l'urlencode. Cependant, je pense que% 20 sera rétro-compatible, comme sous l'ancien standard% 20 travaillé, ce n'était tout simplement pas le cas. préféré. Essayez si vous êtes prêt à jouer, dites-nous comment cela a fonctionné pour vous.

En gros, vous devriez vous en tenir à raw, à moins que votre système EBCDIC ne vous déteste vraiment. La plupart des programmeurs ne se heurteront jamais à l’EBCDIC sur un système créé après l’an 2000, voire 1990 (c’est poussant, mais toujours probable à mon avis).

209
Incognito
echo rawurlencode('http://www.google.com/index.html?id=asd asd');

les rendements

http%3A%2F%2Fwww.google.com%2Findex.html%3Fid%3Dasd%20asd

tandis que

echo urlencode('http://www.google.com/index.html?id=asd asd');

les rendements

http%3A%2F%2Fwww.google.com%2Findex.html%3Fid%3Dasd+asd

La différence étant le asd%20asd vs asd+asd

urlencode diffère de la RFC 1738 en codant les espaces comme + au lieu de %20

35
jitter

Une raison pratique de choisir l’un sur l’autre est d’utiliser le résultat dans un autre environnement, par exemple JavaScript.

Dans PHP urlencode('test 1') renvoie 'test+1' alors que rawurlencode('test 1') renvoie 'test%201' comme résultat.

Mais si vous avez besoin de "décoder" cela en JavaScript en utilisant decodeURI () function alors decodeURI("test+1") vous donnera "test+1" tandis que decodeURI("test%201") vous donnera "test 1" à la suite.

En d'autres termes, l'espace ("") codé par rlencode en plus ("+") dans PHP ne sera pas correctement décodé par decodeURI en JavaScript .

Dans ce cas, utilisez la fonction rawurlencode PHP.

27
Neven Boyanov

Je crois que les espaces doivent être encodés comme:

  • %20 lorsqu'il est utilisé à l'intérieur d'un composant de chemin d'URL
  • + lorsqu'il est utilisé à l'intérieur d'un composant de chaîne de requête d'URL ou de données de formulaire (voir 17.13.4 Types de contenu de formulaire )

L'exemple suivant montre l'utilisation correcte de rawurlencode et de urlencode :

echo "http://example.com"
    . "/category/" . rawurlencode("latest songs")
    . "/search?q=" . urlencode("lady gaga");

Sortie:

http://example.com/category/latest%20songs/search?q=lady+gaga

Que se passe-t-il si vous encodez le chemin et interrogez les composants de chaîne de caractères à l'envers? Pour l'exemple suivant:

http://example.com/category/latest+songs/search?q=lady%20gaga
  • Le serveur Web recherchera le répertoire latest+songs au lieu de latest songs
  • Le paramètre de chaîne de requête q contiendra lady gaga
20
Salman A

1. Quelles sont exactement les différences et

La seule différence réside dans la façon dont les espaces sont traités:

urlencode - basé sur une implémentation existante convertit les espaces en +

rawurlencode - basé sur RFC 1738 traduit les espaces en% 20

La raison de la différence est que + est réservé et valide (non codé) dans les URL.

2. lequel est préféré?

J'aimerais vraiment voir des raisons de choisir l'une plutôt que l'autre ... Je veux pouvoir en choisir une et l'utiliser indéfiniment avec le moins de tracas possible.

Très bien, j'ai une stratégie simple que je suis en prenant ces décisions et que je partagerai avec vous dans l'espoir que cela puisse vous aider.

Je pense que c’est la spécification HTTP/1.1 RFC 2616 qui appelle " applications tolérantes "

Les clients DEVRAIENT être tolérants dans l'analyse de la ligne d'état et les serveurs lors de l'analyse de la ligne de demande.

Face à de telles questions, la meilleure stratégie consiste toujours à consommer le plus possible et à produire des produits conformes aux normes.

Mon conseil est donc d'utiliser rawurlencode pour produire des chaînes codées conformes à la norme RFC 1738 et d'utiliser urldecode pour être compatible avec les versions antérieures et accueillir tout ce que vous pouvez rencontrer.

Maintenant, vous pouvez juste prendre ma parole pour cela, mais prouvons que nous allons ...

php > $url = <<<'EOD'
<<< > "Which, % of Alice's tasks saw $s @ earnings?"
<<< > EOD;
php > echo $url, PHP_EOL;
"Which, % of Alice's tasks saw $s @ earnings?"
php > echo urlencode($url), PHP_EOL;
%22Which%2C+%25+of+Alice%27s+tasks+saw+%24s+%40+earnings%3F%22
php > echo rawurlencode($url), PHP_EOL;
%22Which%2C%20%25%20of%20Alice%27s%20tasks%20saw%20%24s%20%40%20earnings%3F%22
php > echo rawurldecode(urlencode($url)), PHP_EOL;
"Which,+%+of+Alice's+tasks+saw+$s+@+earnings?"
php > // oops that's not right???
php > echo urldecode(rawurlencode($url)), PHP_EOL;
"Which, % of Alice's tasks saw $s @ earnings?"
php > // now that's more like it

Il semblerait que PHP avait exactement cela à l'esprit, même si je n'ai jamais rencontré personne refusant l'un des deux formats, je ne peux pas penser à une meilleure stratégie à adopter comme stratégie de facto, n'est-ce pas?

nonJoy!

5
nickl-

La différence réside dans les valeurs de retour, à savoir:

rlencode () :

Retourne une chaîne contenant tous les caractères non alphanumériques sauf -_. ont été remplacés par un signe de pourcentage (%) suivi de deux chiffres hexadécimaux et d'espaces codés sous la forme de signes plus (+). Il est codé de la même manière que les données postées d’un formulaire WWW, comme dans le type de média application/x-www-form-urlencoded. Cela diffère du codage "RFC 1738 (voir rawurlencode ()) en ce que, pour des raisons historiques, les espaces sont codés sous la forme de signes plus (+).

rawurlencode () :

Retourne une chaîne contenant tous les caractères non alphanumériques sauf -_. ont été remplacés par un signe de pourcentage (%) suivi de deux chiffres hexadécimaux. Il s'agit de l'encodage décrit dans la "RFC 1738 pour empêcher les caractères littéraux d'être interprétés en tant que délimiteurs d'URL spéciaux et pour protéger les URL contre les altérations causées par les supports de transmission avec conversion de caractères (comme certains systèmes de messagerie).

Les deux sont très similaires, mais le dernier (rawurlencode) remplacera les espaces par un '%' et deux chiffres hexadécimaux, ce qui convient au codage de mots de passe ou autres, où un '+' n'est pas, par exemple:

echo '<a href="ftp://user:', rawurlencode('foo @+%/'),
     '@ftp.example.com/x.txt">';
//Outputs <a href="ftp://user:foo%20%40%2B%25%[email protected]/x.txt">
5
karim79

rlencode : Ceci diffère du codage "RFC 1738 (voir rawurlencode ()) en ce que, pour des raisons historiques, les espaces sont codés sous la forme de signes plus (+).

4
Remus Rusanu

Espaces codés comme %20 vs. +

La plus grande raison pour laquelle j’ai vu utiliser rawurlencode() dans la plupart des cas est que urlencode code les espaces de texte sous la forme + (signes plus), où rawurlencode les code sous la forme courante. %20:

echo urlencode("red shirt");
// red+shirt

echo rawurlencode("red shirt");
// red%20shirt

J'ai spécifiquement vu certains points de terminaison d'API qui acceptent les requêtes de texte codé s'attendent à voir %20 pour un espace et échouent si un signe plus est utilisé à la place. Évidemment, cela va différer entre les implémentations d'API et votre kilométrage peut varier.

2
Jake Wilson

Je crois que urlencode est destiné aux paramètres de requête, alors que le rawurlencode est destiné aux segments de chemin. Ceci est principalement dû à %20 pour les segments de chemin d'accès vs + pour les paramètres de requête. Voir cette réponse qui parle des espaces: Quand encoder de l’espace en plus (+) ou en% 20?

Cependant, %20 fonctionne désormais également dans les paramètres de requête. C'est pourquoi rawurlencode est toujours plus sûr. Toutefois, le signe plus a tendance à être utilisé lorsque l'expérience de l'utilisateur en matière d'édition et de lisibilité des paramètres de requête est importante.

Notez que cela signifie que rawurldecode ne décode pas + en espaces ( http://au2.php.net/manual/en/function.rawurldecode.php ). C'est pourquoi $ _GET est toujours automatiquement passé à travers urldecode, ce qui signifie que + et %20 sont tous deux décodés en espaces.

Si vous voulez que l'encodage et le décodage soient cohérents entre les entrées et les sorties et que vous avez choisi d'utiliser toujours + et non %20 pour les paramètres de requête, alors urlencode convient parfaitement aux paramètres de requête (clé et valeur).

La conclusion est:

Segments de chemin - utilisez toujours rawurlencode/rawurldecode

Paramètres de requête: pour le décodage, utilisez toujours urldecode (fait automatiquement). Pour le codage, rawurlencode ou urlencode convient, choisissez-en un qui soit cohérent, en particulier lorsque vous comparez des URL.

1
CMCDragonkai

simple * rawurlencode le chemin - chemin est la partie avant le "?" - les espaces doivent être codés sous la forme% 20 * urlencode de la chaîne de requête - La chaîne de requête est la partie située après le "?" -les espaces sont mieux encodés car "+" = rawurlencode est généralement plus compatible

0
haysam elmasry