IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

Accent avec strtr en utf-8 pour du rewriting [PHP 5.6]


Sujet :

Langage PHP

  1. #1
    Membre actif
    Avatar de Freedolphin
    Homme Profil pro
    Webmaster
    Inscrit en
    Février 2006
    Messages
    291
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Webmaster
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Février 2006
    Messages : 291
    Points : 225
    Points
    225
    Par défaut Accent avec strtr en utf-8 pour du rewriting
    Bonjour à tous,

    Je viens de basculer une base de données d'un serveur à un autre. Je vous passe les détails pour passer à PHP 5.6 et en plus en UTF-8, ce qui a généré des problèmes d'accents, résolus avec diverses manips.
    Seul petit problème persistant : l'URL rewriting.
    J'utilise quelques lignes de code pour transformer le titre de la page en lien compatible, dont celle-ci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $chaine= strtr($chaine, "âäàéèëêïîôöûüùççÂÀÉÊÏÎÛ", "aaaeeeeiioouuuccaaeeiiu");
    Or j'obtiens dans l'url des remplacements bizarres, comme la lettre accentuée "é" qui devient "ui".
    Je ne pense pas que strtr soit obsolète avec PHP 5.6, donc y a-t-il une explication et une astuce pour contourner ce problème ?
    Evidemment, avec str_replace je peux contourner le problème, mais j'aimerais bien comprendre pourquoi strtr réagit ainsi ?

    Merci d'avance !

    EDIT :
    Je viens de trouver une solution, que je laisse ici pour quelqu'un qui rencontrerait le même problème :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $chaine= strtr(utf8_decode($chaine), utf8_decode("âäàéèëêïîôöûüùççÂÀÉÊÏÎÛ"), "aaaeeeeiioouuuccaaeeiiu");
    Le mieux n'est pas forcément l'ennemi du bien.

  2. #2
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    en UTF-8, il faut utiliser mb_strstr(). (mb_ = multi bits)


  3. #3
    Membre actif
    Avatar de Freedolphin
    Homme Profil pro
    Webmaster
    Inscrit en
    Février 2006
    Messages
    291
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Webmaster
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Février 2006
    Messages : 291
    Points : 225
    Points
    225
    Par défaut
    Ah ok, j'ignorais cette fonction.
    Merci beaucoup !
    Le mieux n'est pas forcément l'ennemi du bien.

  4. #4
    Membre actif
    Avatar de Freedolphin
    Homme Profil pro
    Webmaster
    Inscrit en
    Février 2006
    Messages
    291
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Webmaster
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Février 2006
    Messages : 291
    Points : 225
    Points
    225
    Par défaut
    Après un petit test de la fonction mb_strstr, je n'ai plus aucun caractère affiché...
    Le mieux n'est pas forcément l'ennemi du bien.

  5. #5
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 858
    Points : 6 556
    Points
    6 556
    Par défaut
    Citation Envoyé par Freedolphin Voir le message
    J'utilise quelques lignes de code pour transformer le titre de la page en lien compatible, dont celle-ci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $chaine= strtr($chaine, "âäàéèëêïîôöûüùççÂÀÉÊÏÎÛ", "aaaeeeeiioouuuccaaeeiiu");
    Or j'obtiens dans l'url des remplacements bizarres, comme la lettre accentuée "é" qui devient "ui".
    Je ne pense pas que strtr soit obsolète avec PHP 5.6, donc y a-t-il une explication et une astuce pour contourner ce problème ?
    Evidemment, avec str_replace je peux contourner le problème, mais j'aimerais bien comprendre pourquoi strtr réagit ainsi ?
    L'explication est simple. La majorité des fonctions de base ayant trait aux chaînes de caractères en php ont été pensées pour des chaînes dont chaque caractère est codé sur un seul octet ("byte" en anglais, 1 octet = 8 bits, 255 = \xFF = 11111111b). La raison est qu'à l'époque on utilisait des codepages comme ISO-8859-1 pour avoir les accents et caractères propres à sa langue et à sa culture. Ces codepages ont en commun la plage ASCII de 0 à 128 (x00 à x7f), ce qui laisse de 129 à 255 (x80 à xff) pour caser les lettres accentuées, des symboles, des signes de ponctuations propres, des fractions, exposants...

    Puis le standard unicode s'est imposé et avec lui les encodages UTF (UTF-8, UTF-16, UTF-32). Là, il ne s'agit plus de se contenter de représenter uniquement les caractères de son village avec un codepage de 256 caractères, mais bien tous les caractères possibles. Donc, comme pour ce faire un seul octet est insuffisant (car il y a bien plus que 256 caractères différents), les encodages UTF-x peuvent utiliser plusieurs octets pour représenter un seul caractère.

    Donc, lorsque l'on manipule une chaîne encodée en UTF-8, tout va bien dés lors qu'on reste dans la plage ASCII (de U+0000 à U+007F) car chaque caractère est codé sur un seul octet (La plage ASCII est restée commune aux anciennes codepages et à l'UTF-8), mais par contre si on sort de cette plage et que l'on veut manipuler des chaînes contenant des caractères codés sur plusieurs octets, c'est fonctions php continuent de fonctionner octet par octet et provoquent des résultats inattendus. C'est le cas de strtr (du moins pour une de ces 2 syntaxes), mais par exemple aussi pour strlen:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    echo strlen('à'); // résultat: 2 // car "à" est codé sur les deux octets: \xC3 \xA0
    Que ce passe-t-il avec strtr dans ton exemple? La correspondance entre les caractères n'est pas ce que tu crois, car il s'agit de correspondances entre les octets des deux chaînes et pas entre les lettres des deux chaînes:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // â : C3 A2
    // ä : C3 A4
    // à : C3 A0
    // é : C3 A9
    // ...
    // a : 61
    // a : 61
    // a : 61
    // e : 65
    // ...
    //
    // ce qui donne comme correspondance:
    // C3 => 61 (a)
    // A2 => 61 (a)
    // C3 => 61 (a)
    // A4 => 65 (e)
    // ...
    Il faut savoir que strtr a une autre syntaxe qui lui permet d'associer des chaînes de caractères à d'autres chaînes de caractères via un tableau associatif:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $corr = [
        'â' => 'a',
        'ä' => 'a',
        'à' => 'a',
        'é' => 'e',
        'jambon' => 'saucisse',
        //...
    ];
     
    $str = strtr($str, $corr);
    Cette deuxième syntaxe évite les décalages d'octets contrairement à la première. (À noter qu'on pourrait faire quelque chose d'équivalent avec str_replace sauf que strtr ne parse la chaîne qu'une seule fois et choisit toujours la correspondance la plus grande alors que str_replace la parse une fois pour chaque couple et applique les remplacements dans l'ordre).

    Pour enlever les accents d'une chaîne, utiliser strtr n'est pas la bonne méthode.
    Il y a tout d'abord iconv:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $str = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $str)
    Si iconv ne donne pas le résultat escompté, il faut au préalable régler tes locales sur autre chose que C ou POSIX, comme par exemple fr_FR ou en_US: setlocale('LC_CTYPE', 'fr_FR');. Ensuite pour changer la casse, il suffit d'utiliser strtolower.

    Mais il y a mieux: l'extension intl et sa classe Transliterator qui permet de réaliser plusieurs opérations d'un seul coup (normalisation, translittération, changement de casse, filtrage):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $str = "Marie Louîse ma\xCC\x82nge \xCE\xB4es Smarties"; //Marie Louîse ma[accent circonflexe combinant]nge [delta grec]es Smarties
    $trans = Transliterator::createFromRules(":: NFKC; :: Any-Latin; :: Latin-ASCII; :: lower;");
    echo $trans->transliterate($str); // marie louise mange des smarties
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  6. #6
    Membre actif
    Avatar de Freedolphin
    Homme Profil pro
    Webmaster
    Inscrit en
    Février 2006
    Messages
    291
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Webmaster
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Février 2006
    Messages : 291
    Points : 225
    Points
    225
    Par défaut
    Merci pour cette réponse très complète et instructive. J'ai enfin compris le pourquoi du comment !
    Je vais étudier ça de près pour améliorer mon code.
    Merci encore !
    Le mieux n'est pas forcément l'ennemi du bien.

  7. #7
    Membre actif
    Avatar de Freedolphin
    Homme Profil pro
    Webmaster
    Inscrit en
    Février 2006
    Messages
    291
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Webmaster
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Février 2006
    Messages : 291
    Points : 225
    Points
    225
    Par défaut
    Je vais en effet retenir la classe Transliterator, même si je peine à adapter le code en mode procédural.
    A moins que le mélange procédural / orienté objet sur une même fonction ne pose pas de problème éthique ?
    Le mieux n'est pas forcément l'ennemi du bien.

  8. #8
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 858
    Points : 6 556
    Points
    6 556
    Par défaut
    Non, il n'y a pas de "problèmes éthiques" à le faire, c'est juste un peu curieux de passer de l'un à l'autre.
    Je pense qu'il y a une erreur dans le manuel à propos de la signature de la fonction procédurale transliterator_create_from_rules qui à mon avis a la même signature que la méthode objet. Ceci fonctionne parfaitement chez moi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $trans = transliterator_create_from_rules(":: NFKC; :: Any-Latin; :: Latin-ASCII; :: lower;");
    echo transliterator_transliterate($trans, $str);
    Quoiqu'il en soit, même dans cette version, $trans reste une instance de la classe Transliterator.

    [Edit] En fait non, les deux signatures sont fausses dans le manuel, il faut faire un mix des deux pour avoir la bonne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // Style orienté objet
    public static Transliterator Transliterator::createFromRules ( string $rules [, int $direction ] )
     
    // Style procédural
    Transliterator transliterator_create_from_rules ( string $rules [, int $direction ] )
     
    // ------------------------------------------------
    // Liste de paramètres ¶
    // ------------------------------------------------
    // rules
    //    Les règles.
     
    // direction
    //    La direction, par défaut >Transliterator::FORWARD (qui vaut int(0)). Peut également être défini à Transliterator::REVERSE (qui vaut int(1)).
     
    // ------------------------------------------------
    // Valeurs de retour ¶
    // ------------------------------------------------
    // Retourne un objet Transliterator en cas de succès, ou NULL si une erreur survient.
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  9. #9
    Membre actif
    Avatar de Freedolphin
    Homme Profil pro
    Webmaster
    Inscrit en
    Février 2006
    Messages
    291
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Webmaster
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Février 2006
    Messages : 291
    Points : 225
    Points
    225
    Par défaut
    Ah bah si le manuel se trompe, qu'est-ce qu'on va devenir ?
    Bon enfin ton code procédural fonctionne aussi parfaitement chez moi, je le garde et je te remercie encore vivement !
    Très bon week-end.
    Le mieux n'est pas forcément l'ennemi du bien.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 0
    Dernier message: 27/08/2013, 16h23
  2. [MySQL] Afficher des accents avec UTF-8
    Par ceweb dans le forum PHP & Base de données
    Réponses: 11
    Dernier message: 23/03/2009, 10h41
  3. Affichage des accents avec UTF-8
    Par jlb59 dans le forum Balisage (X)HTML et validation W3C
    Réponses: 14
    Dernier message: 18/12/2007, 19h56
  4. [utf-8] problème d'accents avec gtk+
    Par over_score dans le forum Général Python
    Réponses: 3
    Dernier message: 14/12/2005, 01h43
  5. Réponses: 12
    Dernier message: 26/04/2004, 08h32

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo